题意:给一个长度<=15的串S(仅包含ACGT),对于所有的i(0<=i<=|S|),求有多少长度为M的串T满足串T和串S的最长公共子序列长度为i。
这个题让我想起了之前做过的一个数位dp,问有多少个数的数字组成的最长上升子序列长度为x。那个题中显然最长上升子序列不超过10,我们用状态压缩来模拟那个做lis时的栈即可。
这个题|S|<=15,一样用状态压缩来表示那些字母可以包含在lcs中。注意求lcs的经典n^2的dp实际是在线的,即添加一个T中的字符就可以求出与S形成lcs。我们对所有状态拓展出它加上一个字符后新形成的状态即可,注意需要将二进制状态还原并暴力跑一次lcs再转为新状态。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define erp(i,a,b) for(int i=a;i>=b;--i)
#define LL long long
using namespace std;
const int mo = 1000000007;
const char str[] = {'A','C','G','T'};
inline void up(int&x,int y){x+=y;if(x>=mo)x-=mo;}
int cas, n, m;
char s[20];
int trans[1<<15][4], f[2][1<<15];
void prep()
{
int f[20], g[20];
for (int i = 0; i<1<<n; ++i)
{
memset(f,0,sizeof f);
memset(g,0,sizeof g);
rep(j,1,n) f[j]=f[j-1]+(i>>(j-1)&1);
for (int k=0; k<4; ++k)
{
rep(j,1,n)
{
g[j]=max(g[j-1], f[j]);
if (str[k]==s[j]) g[j]=max(g[j], f[j-1]+1);
}
trans[i][k]=0;
rep(j,1,n) if(g[j]-g[j-1]) trans[i][k] |= 1<<j-1;
}
}
}
int ans[20];
int main()
{
scanf("%d",&cas);
while (cas--)
{
scanf("%s%d", s+1, &m);
n=strlen(s+1);
prep();
memset(f,0,sizeof f);
f[0][0]=1;
rep(i, 1, m)
{
int c = i&1;
memset(f[c], 0, sizeof f[c]);
for (int s=0;s<1<<n;++s) rep(j,0,3)
up(f[c][trans[s][j]], f[c^1][s]);
}
memset(ans,0,sizeof ans);
for(int s=0;s<(1<<n);++s)
up(ans[__builtin_popcount(s)], f[m&1][s]);
rep(i,0,n)printf("%d\n",ans[i]);
}
return 0;
}