版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/qq_35950004/article/details/87863099
题意:求长度为m的,字符集大小为4的,字符串,中,与字符串S(|S|<=15)的最长公共子序列长度=i的字符串数量。
发现这个状态十分不好定义,考虑
LCS的过程:
发现
或
那么我们完全可以把长度为i的字符串与S,DP后
的值用二进制状态压缩起来,每次枚举新加入的字符,再进行LCS的dp,得到新状态转移。
意思就是,LCS和计数这两个dp不是不可得兼,用状态存状压后LCS的dp值,实际的dp值计数。类似的还有用
单调栈求LIS的奇妙贪心做状态,实际的dp值计数的题目(HDU 4352 LIS + 数位DP)。
这类题目的存在说明了dp套dp不是ddp,不是动态2规划,但也是毒瘤题。
AC Code:
#include<bits/stdc++.h>
#define maxn 16
#define mod 1000000007
using namespace std;
char s[maxn],mc[5]="ATGC";
int m,nxt[30][1<<15],f[2][1<<15],ans[maxn];
void add(int &a,int b){ a=a+b;a>=mod?a-=mod:0; }
int main()
{
int T;
for(scanf("%d",&T);T--;)
{
scanf("%s%d",s,&m);
int n = strlen(s);
for(int i=0;i<4;i++)
for(int sta=0;sta<(1<<n);sta++)
{
int nsta=0;
for(int j=0,fpd=0,fd=0,fu=0,fpu=0;j<n;j++)
{
fpd=fd;
fd += (sta>>j&1);
fpu=fu;
fu = max(fu , fd);
if(mc[i] == s[j]) fu = max(fu,fpd+1);
nsta |= (fu-fpu)<<j;
}
nxt[i][sta]=nsta;
}
int now = 1,pre = 0;
memset(f[pre],0,sizeof f[pre]);
f[pre][0] = 1;
for(;m--;swap(now,pre))
{
memset(f[now],0,sizeof f[now]);
for(int sta=0;sta<(1<<n);sta++)
if(f[pre][sta])
for(int i=0;i<4;i++)
add(f[now][nxt[i][sta]] , f[pre][sta]);
}
memset(ans,0,sizeof ans);
for(int sta=0;sta<(1<<n);sta++)
add(ans[__builtin_popcount(sta)] , f[pre][sta]);
for(int i=0;i<=n;i++)
printf("%d\n",ans[i]);
}
}