题目大意
给出n个模板串,如果在一个文本中至少存在一个模板串,那么这个文本就是合法的,求长度为m的文本的合法方案数模10007后的结果。
解题报告
AC自动机+DP的经(mo)典(ban)题。正难则反,答案为 26m 26 m 减去一个都匹配不到的方案。用模板串建AC自动机, f[i][j] f [ i ] [ j ] 表示文本串长度为 i i ,在AC自动机上匹配到节点的方案数,注意别走到单词节点,而且如果 fail[x] f a i l [ x ] 是匹配的话,那么x也匹配。构造fail函数时加一道就行了。
示例代码
#include<cstdio>
#include<cstring>
using namespace std;
const int tt=10007;
int n,k,len,ans,ch[6005][26],nxt[6005],lst[6005],que[6005],f[105][6005];
bool vs[6005];
char s[105];
struct AC{
void clear(int x){memset(ch[x],0,sizeof(ch[x])); nxt[x]=lst[x]=0;}
void insist(){
int now=0,L=strlen(s);
for (int i=0;i<L;i++){
if (!ch[now][s[i]-'A']) {ch[now][s[i]-'A']=++len; clear(len);}
now=ch[now][s[i]-'A'];
}
vs[now]=1;
}
void makeF(){
int hed=0,til=0;
for (int i=0;i<26;i++)
if (ch[0][i]){
que[++til]=ch[0][i];
nxt[ch[0][i]]=lst[ch[0][i]]=0;
}
while (hed!=til){
int x=que[++hed]; vs[x]|=vs[nxt[x]];
for (int i=0;i<26;i++)
if (ch[x][i]){
int now=ch[x][i]; nxt[now]=ch[nxt[x]][i];
lst[now]=(vs[nxt[now]])?nxt[now]:lst[nxt[now]];
que[++til]=now;
}else ch[x][i]=ch[nxt[x]][i];
}
}
}tr;
int main()
{
freopen("text.in","r",stdin);
freopen("text.out","w",stdout);
scanf("%d%d",&n,&k); len=0; memset(vs,0,sizeof(vs));
for (int i=1;i<=n;i++) {scanf("%s",s); tr.insist();}
tr.makeF(); memset(f,0,sizeof(f)); f[0][0]=1;
for (int i=0;i<k;i++)
for (int j=0;j<=len;j++)
if (f[i][j]&&!vs[j])
for (int t=0;t<26;t++)
if (!vs[ch[j][t]]) f[i+1][ch[j][t]]=(f[i+1][ch[j][t]]+f[i][j])%tt;
ans=1; for (int i=1;i<=k;i++) ans=(ans*26)%tt;
for (int i=0;i<=len;i++) if (!vs[i]) ans=(ans+tt-f[k][i])%tt;
printf("%d",ans);
return 0;
}