bzoj 3864 Hero meet devil

版权声明:本文为博主原创文章,未经博主允许也可以转载。 https://blog.csdn.net/qq_35541672/article/details/85259978

题意

一个脱氧核苷酸链只由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]);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_35541672/article/details/85259978