大致题意: 给定一个字符串,对于每一个(i),问有多少由"A","G","T","C"组成的长度为(m)的字符串和给定串的最长公共子序列长度为(i)。
双倍经验
可以去看看【洛谷4590】[TJOI2018] 游园会,就会发现与那题相比此题甚至还要少一个限制。
这里直接给出代码了。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define M 15
#define X 1000000007
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
using namespace std;
int n,l,s[M+5];char st[M+5];
class LCS_Automation
{
private:
int lim,a[M+5],f[M+5],S[1<<M][4],cnt[1<<M],dp[2][1<<M];
I void Extend(CI x)//求出x的后继状态
{
RI i;for(i=0;i^l;++i) a[i+1]=a[i]+((x>>i)&1);//解码
RI j,t;for(i=0;i^4;++i)//枚举加入字符
{
for(j=1;j<=l;++j) f[j]=max(max(a[j],f[j-1]),a[j-1]+(s[j]==i));//DP
for(t=j=0;j^l;++j) t|=(f[j+1]-f[j])<<j;S[x][i]=t;//状压差值
}
}
public:
I void Build() {lim=1<<l;for(RI i=0;i^lim;++i) Extend(i);}//建立LCS自动机
int ans[M+5];I void DP()//LCS自动机上DP
{
#define Cls(t) memset(dp[t],0,sizeof(dp[t]))
RI i,j,k,t;for(Cls(0),dp[0][0]=1,t=i=0;i^n;++i,t^=1)//注意滚存
for(Cls(t^1),j=0;j^lim;++j) for(k=0;k^4;++k) Inc(dp[t^1][S[j][k]],dp[t][j]);//枚举加入字符转移
for(memset(ans,0,sizeof(ans)),i=0;i^lim;++i)//显然cnt[i]就是状态i的LCS
cnt[i]=cnt[i>>1]+(i&1),Inc(ans[cnt[i]],dp[t][i]);//统计答案
}
}A;
int main()
{
RI Tt,i;scanf("%d",&Tt);W(Tt--)
{
scanf("%s%d",st+1,&n),l=strlen(st+1);
for(i=1;i<=l;++i) s[i]=st[i]^'A'?(st[i]^'G'?(st[i]^'T'?3:2):1):0;//把字符改为数码
for(A.Build(),A.DP(),i=0;i<=l;++i) printf("%d
",A.ans[i]);//建自动机,然后自动机上DP
}return 0;
}