【[JSOI2007]文本生成器】

\(AC\)机上的计数\(dp\)

并没有想到反着求出不合法的串的个数,直接正面硬上

\(dp[i][j][0/1]\)表示匹配出的长度为\(i\),在\(AC\)机上位置为\(j\),没有/有匹配到一个完整串的方案数

由于这个的长度是满足子结构的,可以直接\(dp\)求解

注意结束标记会影响到所有能通过跳\(fail\)到达它的点,所以\(Build\)的时候预处理好

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define re register
#define maxn 60*105
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
const int mod=10007;
int fail[maxn],son[maxn][26],flag[maxn];
inline int read()
{
	char c=getchar();
	int x=0;
	while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();
	return x;
}
char S[1005];
int cnt,n,m;
int dp[105][maxn][2];
inline void ins()
{
	scanf("%s",S+1);
	int len=strlen(S+1),now=0;
	for(re int i=1;i<=len;i++)
	{
		if(!son[now][S[i]-'A']) son[now][S[i]-'A']=++cnt;
		now=son[now][S[i]-'A'];
	}
	flag[now]=1;
}
inline void Build()
{
	std::queue<int> q;
	for(re int i=0;i<26;i++) if(son[0][i]) q.push(son[0][i]);
	while(!q.empty())
	{
		int k=q.front();
		q.pop();
		flag[k]|=flag[fail[k]];
		for(re int i=0;i<26;i++)
		if(son[k][i]) fail[son[k][i]]=son[fail[k]][i],q.push(son[k][i]);
			else son[k][i]=son[fail[k]][i];
	}
}
int main()
{
	n=read(),m=read();
	for(re int i=1;i<=n;i++) ins();
	Build();
	dp[0][0][0]=1;
	for(re int i=0;i<m;i++)
		for(re int j=0;j<=cnt;j++)
			for(re int o=0;o<2;o++)
			{
				if(!dp[i][j][o]) continue;
				for(re int k=0;k<26;k++)
					dp[i+1][son[j][k]][o|flag[son[j][k]]]+=dp[i][j][o],dp[i+1][son[j][k]][o|flag[son[j][k]]]%=mod;
			}
	int ans=0;
	for(re int i=0;i<=cnt;i++)
		ans+=dp[m][i][1],ans%=mod;
	printf("%d\n",ans);
	return 0;
}
posted @ 2019-01-02 12:20  asuldb  阅读(111)  评论(0编辑  收藏  举报