Description
基因串是由ACGT4个字母组成的,我们有一个长度为n的基因串S。想要知道长度为m的基因串中,与S的最长公共子序列分别为0; 1; ; n的串各有几个。
输出答案关于10^9 + 7的余数。
n<=10;m<=1000
Solution
先看一下暴力,
每次枚举出一个大串b以后,用DP来算:
设
fi,j
表示大串做到位置i,小串匹配到j的最大匹配,
转移:
fi,j=max(fi−1,j,fi,j−1,fi−1,j−1+[aj=bi])
仔细观察,发现对于相同的i,如对于每一个j,
fi,j
都相同,那么这两个
fi
对后面的影响都是一样的,哪怕b不一样;
还有性质,对于固定的i,
fi,j
是不下降的,且上升最多+1,所以可以把它差分以后压成一个01串;
设
Fi,S
表示b串枚举到i位,S为压缩的
fi,j
的状态,值表示方案数,
每次转移枚举这位选什么,可以计算出当前位选了这个数后的新的S,然后更新;
细节蛮多的,读者自己想一下吧
复杂度: O(m2n∗4)
Code
#include <iostream>
#include <cstdio>
#include <cstdlib>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long LL;
const int N=1005,M=12,mo=1e9+7;
int m,n;
int a[N],g[M];
LL f[N][2125];
LL ans[N];
int er[M+1];
int f1[2050][4],f2[2050];
int main()
{
int q,w;
er[1]=1;fo(i,2,12)er[i]=er[i-1]<<1;
char ch=' ';
for(;ch!='A'&&ch!='C'&&ch!='G'&&ch!='T';ch=getchar());
for(;ch=='A'||ch=='C'||ch=='G'||ch=='T';ch=getchar())
{
if(ch=='A')a[++n]=1;
if(ch=='C')a[++n]=2;
if(ch=='G')a[++n]=3;
if(ch=='T')a[++n]=4;
}
fo(i,0,er[n+1]-1)
fo(j,1,4)
{
fo(k,1,n)g[k]=g[k-1]+((er[k]&i)?1:0);
f2[i]=g[n];
q=0;
fod(k,n,1)g[k]=max(g[k],g[k-1]+(j==a[k]));
fo(k,1,n)g[k]=max(g[k],g[k-1]);
q=0;
fo(k,1,n)if(g[k]!=g[k-1])q+=er[k];
f1[i][j]=q;
}
scanf("%d",&m);
f[0][0]=1;
fo(I,0,m-1)
fo(i,0,er[n+1]-1)if(f[I][i])
{
fo(j,1,4)
{
q=f1[i][j];
f[I+1][q]=(f[I+1][q]+f[I][i])%mo;
}
}
fo(i,0,er[n+1]-1)ans[f2[i]]=(ans[f2[i]]+f[m][i])%mo;
fo(i,0,n)printf("%lld\n",ans[i]);
return 0;
}