【题解】
与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;
}