CF86C题解

麻了,最近几天脑子频繁出问题。。。

对于一个 DNA 序列,其一定有若干个前缀满足这些前缀也是 DNA 序列。

在一个前缀后面拼上一个能够匹配得上的 \(s\) 也能得到一个 DNA 序列。

考虑 DP。设 \(dp[i][u][k]\) 表示填了 \(i\) 个字符,当前在 ACAM 上匹配到第 \(u\) 个节点,且当前串的前缀 \(S[i-k]\) 是一个 DNA 序列。

有了这个就可以就可以转移啦。记得需要去掉不合法的状态。

对每一个节点维护一个可能成为答案的 \(k\) 的范围 \(len[u]\) 然后统计即可。

#include<cstdio>
typedef unsigned ui;
const ui M=105,mod=1e9+9;
ui n,m,t,tot(1),d[M],len[M],fail[M],trans[M][4],dp[1005][M][15];ui s[M];char S[15];
ui L,R,q[M];
inline ui max(const ui&a,const ui&b){
	return a>b?a:b;
}
inline void Insert(ui*s){
	ui i,u(1);
	for(i=0;~s[i];++i){
		const ui&c=s[i];
		if(!trans[u][c])trans[u][c]=++tot;
		u=trans[u][c];
	}
	len[u]=i;
}
inline void Build(){
	L=1;
	for(ui c=0;c<4;++c){
		if(trans[1][c])q[++R]=trans[1][c],fail[trans[1][c]]=1,d[q[R]]=1;
		else trans[1][c]=1;
	}
	while(L<=R){
		const ui&u=q[L++];
		for(ui c=0;c<4;++c){
			if(trans[u][c])q[++R]=trans[u][c],fail[trans[u][c]]=trans[fail[u]][c],d[q[R]]=d[u]+1;
			else trans[u][c]=trans[fail[u]][c];
		}
	}
	for(ui i=1;i<=R;++i)len[q[i]]=max(len[q[i]],len[fail[q[i]]]);
}
signed main(){
	ui ans(0);
	scanf("%u%u",&n,&m);
	for(ui i=1;i<=m;++i){
		scanf("%s",S);
		ui j(0);while(S[j])s[j]=S[j]=='A'?0:S[j]=='C'?1:S[j]=='T'?2:3,++j;s[j]=-1;
		Insert(s);if(j>t)t=j;
	}
	Build();
	for(ui c=0;c<4;++c)dp[1][trans[1][c]][1-len[trans[1][c]]]=1;
	for(ui i=2;i<=n;++i){
		for(ui u=1;u<=tot;++u)for(ui k=0;k<=d[u];++k){
			for(ui c=0;c<4;++c)dp[i][trans[u][c]][k+1]=(dp[i][trans[u][c]][k+1]+dp[i-1][u][k])%mod;
		}
		for(ui u=1;u<=tot;++u){
			for(ui k=1;k<=len[u];++k)dp[i][u][0]=(dp[i][u][0]+dp[i][u][k])%mod,dp[i][u][k]=0;
		}
	}
	for(ui u=1;u<=tot;++u)for(ui k=0;k<=len[u];++k)ans=(ans+dp[n][u][k])%mod;
	printf("%u",ans);
}
posted @ 2022-03-27 19:27  Prean  阅读(43)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};