题意
一个脱氧核苷酸链只由AGCT组成,以下讨论的串都只有AGCT
现在给出一个串S,问你对于i∈[0,|S|]有多少个长度为m的串T满足LCS(S,T)=i
|S|<=15 m<=1000
题解
直观的想法是dp套kmp?
反正下面讲的是dp套dp
内层的dp是个显然的LCS
盗个图
我们观察这个转移式,可以发现只会用到两行的信息,而且同一行相邻两个之间相差不超过1
那就很好办了,我们把一行的信息差分,就能得到一个长度为m的01串,然后就可以把它状压
那么
d
[
i
]
[
t
(
S
,
c
)
]
+
=
d
[
i
−
1
]
[
S
]
d[i][t(S,c)]+=d[i-1][S]
d[i][t(S,c)]+=d[i−1][S]
其中t(S,c)表示上一行是S,当前字符为c,所得到的状态
这个可以预处理出来。预处理的具体方式就是先把状态还原成字符串,再进行一个单行的LCS,再进行压缩
总的复杂度
O
(
4
T
2
N
M
)
O(4T2^NM)
O(4T2NM)
怎么看都不科学…但是能过…
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int L=1005,N=17,M=(1<<15)+5;
const int mod=1e9+7;
int n,m;
char s1[L];
int s[L];
int t[M][4];
int tot;
int p[2][N];
int d[L][M];
int cnt[M];
int ans[N];
int lowbit(int x){
return x&(-x);
}
void Init(){
for(int S=0;S<=tot;S++)
for(int c=0;c<4;c++){
memset(p,0,sizeof p);
for(int i=0;i<n;i++)
p[0][i+1]=p[0][i]+((S>>i)&1);
for(int i=1;i<=n;i++){
int tmp=0;
if(s[i]==c)
tmp=max(tmp,max(p[0][i],p[0][i-1]+1));
tmp=max(tmp,max(p[0][i],p[1][i-1]));
p[1][i]=tmp;
}
t[S][c]=0;
for(int i=0;i<n;i++)
t[S][c]+=(p[1][i+1]-p[1][i])<<i;
}
}
void dp(){
memset(d,0,sizeof d);
d[0][0]=1;
for(int i=1;i<=m;i++)
for(int S=0;S<=tot;S++)
for(int c=0;c<4;c++){
int T=t[S][c];
d[i][T]=(d[i][T]+d[i-1][S])%mod;
}
memset(ans,0,sizeof ans);
for(int S=0;S<=tot;S++)
ans[cnt[S]]=(ans[cnt[S]]+d[m][S])%mod;
}
int main()
{
for(int i=1;i<M;i++)
cnt[i]=cnt[i^lowbit(i)]+1;
int Kase;
scanf("%d",&Kase);
while(Kase--){
scanf("%s",s1+1);
n=strlen(s1+1);
tot=(1<<n)-1;
for(int i=1;i<=n;i++){
if(s1[i]=='A')
s[i]=0;
else if(s1[i]=='C')
s[i]=1;
else if(s1[i]=='G')
s[i]=2;
else
s[i]=3;
}
Init();
scanf("%d",&m);
dp();
for(int i=0;i<=n;i++)
printf("%d\n",ans[i]);
}
}