给一个长度不超过15的字符串S,问长度为m且与S的最长公共子序列为0~len(S)的字符串各有多少个?字符串中只会出现A,C,G,T四种字符。
定义状态dp[i][j],i表示长度为i的字符串,j为压缩了的状态,表示该字符串与串S的前k位的最长公共子序列的长度。注意到任意一个字符串与S的位k为的最长公共子序列的长度,和他与S的前k+1位的最长公共子序列的长度最多只差1,所以可以压缩成一个长度不超过15的01串,表示出那个长度为i的串与S的任意前缀最长公共子序列的长度情况。dp[i][j]存储在在i,j情况下的不同的字符串的个数。
提前预处理出每个j在加上ACTG中的任意一种字符之后会转移到什么状态。最后扫描dp[m][j],求出结果。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int mod=1000000007;
inline int getNbits(int x) {
int ans=0;
while (x) {
ans+=x&1;
x>>=1;
}
return ans;
}
inline void update(int &a,int b) {
a+=b;
if (a>mod) a-=mod;
}
char s[16];
int ans[16];
int nbits[1<<15];
int nxt[1<<15][4];
int dp[1001][1<<15];
int ls,n,m;
int getNext(int o,char c) {
int tmpdp[16],tmpdp2[16],ans=0,i;
tmpdp[0]=0;tmpdp2[0]=0;
for (i=0;i<ls;i++) {
if (o&1<<i) tmpdp[i+1]=tmpdp[i]+1;
else tmpdp[i+1]=tmpdp[i];
}
for (i=0;i<ls;i++) {
if (c==s[i]) tmpdp2[i+1]=tmpdp[i]+1;
else tmpdp2[i+1]=max(tmpdp2[i],tmpdp[i+1]);
}
for (i=0;i<ls;i++) {
if (tmpdp2[i+1]!=tmpdp2[i]) ans|=1<<i;
}
return ans;
}
void init() {
int j;
for (j=0;j<n;j++) {
nxt[j][0]=getNext(j,'A');
nxt[j][1]=getNext(j,'C');
nxt[j][2]=getNext(j,'G');
nxt[j][3]=getNext(j,'T');
}
}
int main() {
int t,i,j;
scanf("%d",&t);
for (i=0;i<1<<15;i++) nbits[i]=getNbits(i);
while (t--) {
scanf("%s",s);
scanf("%d",&m);
ls=strlen(s);
n=1<<ls;
init();
for (i=0;i<=m;i++)
for (j=0;j<n;j++)
dp[i][j]=0;
dp[0][0]=1;
for (i=0;i<m;i++) {
for (j=0;j<n;j++) {
update(dp[i+1][nxt[j][0]],dp[i][j]);
update(dp[i+1][nxt[j][1]],dp[i][j]);
update(dp[i+1][nxt[j][2]],dp[i][j]);
update(dp[i+1][nxt[j][3]],dp[i][j]);
}
}
memset(ans,0,sizeof(ans));
for (j=0;j<n;j++) {
update(ans[nbits[j]],dp[m][j]);
}
for (i=0;i<=ls;i++) {
printf("%d\n",ans[i]);
}
}
return 0;
}