您的位置:首页 > Web前端 > JavaScript

BZOJ1030 [JSOI2007]文本生成器(AC自动机+dp)

2015-06-24 18:09 218 查看
【题解】

与poj2778有类似之处,只不过本题模板串太长,无法用到矩阵,而文本较短,适于dp

ans = 26^m - 不含任意单词的文本数

不含任意单词的文本数 的求法:

转化成从有向图的一点出发,走n步到达另一结点的方案数

本题为 从字典树的root出发,走m步到达任一结点,且不构成单词 的方案数,需使建立的所有有向边合法(无法走出单词)

将单词建成AC自动机,每个结点u都连向 它的26个后继ch[u][i],或它在fail树上的祖先的后继ch[fa][i],使用改进过的AC自动机,就可以统一写成ch[u][i]

然后标记所有词尾结点,及失配指针指向词尾结点的点,以后用到路径时不考虑含有这些点的边

若结点少的话,就求出图对应的邻接矩阵A,然后用快速幂求A^m

否则,设f[i][j]表示用i步能从root走到j的方案数,按照点之间同样的连接关系转移,按步数从小到大做dp就行了

【代码】

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MOD 10007
int ch[6005][30],no[6005],f[6005],q[6005],dp[105][6005];
char s[105];
int sz=0;
void tj()
{
	int i,u=0,len=strlen(s);
	for(i=0;i<len;i++)
	{
		if(ch[u][s[i]-64]==0) ch[u][s[i]-64]=++sz;
		u=ch[u][s[i]-64];
	}
	no[u]=1;
}
void build()
{
	int i,head=0,tail=0,u,v;
	for(i=1;i<=26;i++)
		if(ch[0][i]>0) q[tail++]=ch[0][i];
	while(head<tail)
	{
		u=q[head++];
		no[u]|=no[f[u]];
		for(i=1;i<=26;i++)
		{
			v=ch[u][i];
			if(v>0)
			{
				f[v]=ch[f[u]][i];
				q[tail++]=v;
			}
			else ch[u][i]=ch[f[u]][i];
		}
	}
}
int main()
{
	int n,m,i,j,k,ans=0,ans2=1;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++)
	{
		scanf("%s",s);
		tj();
	}
	build();
	dp[0][0]=1;
	for(i=1;i<=m;i++)
	{
		for(j=0;j<=sz;j++)
			if(no[j]==0&&dp[i-1][j]>0)
				for(k=1;k<=26;k++)
					dp[i][ch[j][k]]=(dp[i][ch[j][k]]+dp[i-1][j])%MOD;
	}
	for(i=0;i<=sz;i++)
		if(no[i]==0) ans+=dp[m][i];
	for(i=1;i<=m;i++)
		ans2=(ans2*26)%MOD;
	printf("%d",((ans2-ans)%MOD+MOD)%MOD);
	return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: