1. 程式人生 > >bzoj 3864 Hero meet devil

bzoj 3864 Hero meet devil

題意

一個脫氧核苷酸鏈只由AGCT組成,以下討論的串都只有AGCT
現在給出一個串S,問你對於i∈[0,|S|]有多少個長度為m的串T滿足LCS(S,T)=i
|S|<=15 m<=1000

題解

直觀的想法是dp套kmp?
反正下面講的是dp套dp
內層的dp是個顯然的LCS
盜個圖
盜個圖
我們觀察這個轉移式,可以發現只會用到兩行的資訊,而且同一行相鄰兩個之間相差不超過1
那就很好辦了,我們把一行的資訊差分,就能得到一個長度為m的01串,然後就可以把它狀壓
那麼 d [

i ] [ t ( S , c ) ] +
= d [ i 1 ] [ S ]
d[i][t(S,c)]+=d[i-1][S]

其中t(S,c)表示上一行是S,當前字元為c,所得到的狀態
這個可以預處理出來。預處理的具體方式就是先把狀態還原成字串,再進行一個單行的LCS,再進行壓縮
總的複雜度 O ( 4 T 2 N M ) O(4T2^NM)
怎麼看都不科學…但是能過…

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int L=1005,N=17,M=(1<<15)+5;
const int mod=1e9+7;
int n,m;
char s1[L];
int s[L];
int t[M][4];
int tot;
int p[2][N];
int d[L][M];
int cnt[M];
int ans[N];
int lowbit(int x){
    return x&(-x);
}
void Init(){
    for(int S=0;S<=tot;S++)
        for(int c=0;c<4;c++){
            memset(p,0,sizeof p);
            for(int i=0;i<n;i++)
                p[0][i+1]=p[0][i]+((S>>i)&1);
            for(int i=1;i<=n;i++){
                int tmp=0;
                if(s[i]==c)
                    tmp=max(tmp,max(p[0][i],p[0][i-1]+1));
                tmp=max(tmp,max(p[0][i],p[1][i-1]));
                p[1][i]=tmp;
            }
            t[S][c]=0;
            for(int i=0;i<n;i++)
                t[S][c]+=(p[1][i+1]-p[1][i])<<i;
        }
}
void dp(){
    memset(d,0,sizeof d);
    d[0][0]=1;
    for(int i=1;i<=m;i++)
        for(int S=0;S<=tot;S++)
            for(int c=0;c<4;c++){
                int T=t[S][c];
                d[i][T]=(d[i][T]+d[i-1][S])%mod;
            }
    memset(ans,0,sizeof ans);
    for(int S=0;S<=tot;S++)
        ans[cnt[S]]=(ans[cnt[S]]+d[m][S])%mod;
}
int main()
{
    for(int i=1;i<M;i++)
        cnt[i]=cnt[i^lowbit(i)]+1;
    int Kase;
    scanf("%d",&Kase);
    while(Kase--){
        scanf("%s",s1+1);
        n=strlen(s1+1);
        tot=(1<<n)-1;
        for(int i=1;i<=n;i++){
            if(s1[i]=='A')
                s[i]=0;
            else if(s1[i]=='C')
                s[i]=1;
            else if(s1[i]=='G')
                s[i]=2;
            else
                s[i]=3;
        }
        Init();
        scanf("%d",&m);
        dp();
        for(int i=0;i<=n;i++)
            printf("%d\n",ans[i]);
    }
}