Description
JSOI交给队员ZYX一个任务,编制一个称之为“文本生成器”的电脑软件:该软件的使用者是一些低幼人群,
他们现在使用的是GW文本生成器v6版。该软件可以随机生成一些文章―――总是生成一篇长度固定且完全随机的文 章——
也就是说,生成的文章中每个字节都是完全随机的。如果一篇文章中至少包含使用者们了解的一个单词,
那么我们说这篇文章是可读的(我们称文章a包含单词b,当且仅当单词b是文章a的子串)。但是,即使按照这样的
标准,使用者现在使用的GW文本生成器v6版所生成的文章也是几乎完全不可读的?。ZYX需要指出GW文本生成器 v6
生成的所有文本中可读文本的数量,以便能够成功获得v7更新版。你能帮助他吗? Input输入文件的第一行包含两个正整数,分别是使用者了解的单词总数N (<= 60),GW文本生成器 v6生成的文本固
定长度M;以下N行,每一行包含一个使用者了解的单词。这里所有单词及文本的长度不会超过100,并且只可能包 含英文大写字母A..Z
Output一个整数,表示可能的文章总数。只需要知道结果模10007的值。
此题其实和bzoj2938 病毒(点这里)有异曲同工之处。
问题可以转化为建立一个长度为m的字符串,使之不包含任何一个序列有多少种情况。再用总排列减去即可。
还是建立AC自动机以后标记危险结点【详见这里】,然后dp。dp[i][j]表示走i步到j结点的方案数。
因为不记父亲只记儿子,所以用刷表法比较好。
dp[i+1][ch[j][x]]+=dp[i][j],1<=x<=26。
注意即使不存在该结点(或者说连到了根节点上)也要累加,最后也要加上根节点上的dp值。
#include<cstdio>
#include<cstring>
const int md=10007;
int ch[6010][30],fai[6010],dp[6010][6010],m,k,que[6010];
bool b[6010];
char s[110];
int main()
{
int i,j,n,p,q,x,y,z,hd,tl,tot;
scanf("%d%d",&n,&k);
for (i=1;i<=n;i++)
{
scanf("%s",s+1);
p=0;
for (j=1;j<=strlen(s+1);j++)
{
x=s[j]-'A'+1;
if (ch[p][x]) p=ch[p][x];
else p=ch[p][x]=++m;
}
b[p]=1;
}
hd=1;
tl=0;
for (i=1;i<=26;i++)
if (ch[0][i]) que[++tl]=ch[0][i];
while (hd<=tl)
{
p=que[hd++];
for (i=1;i<=26;i++)
if (ch[p][i])
{
x=ch[p][i];
fai[x]=ch[fai[p]][i];
que[++tl]=x;
if (b[fai[x]]) b[x]=1;
}
else
ch[p][i]=ch[fai[p]][i];
}
for (i=1;i<=26;i++)
if (!b[ch[0][i]]) dp[1][ch[0][i]]++;
for (i=0;i<k;i++)
for (j=0;j<=m;j++)
if (dp[i][j])
for (x=1;x<=26;x++)
if (!b[ch[j][x]])
dp[i+1][ch[j][x]]=(dp[i+1][ch[j][x]]+dp[i][j])%md;
tot=0;
for (i=0;i<=m;i++)
tot=(tot+dp[k][i])%md;
x=1;
for (i=1;i<=k;i++)
x=(x*26)%md;
printf("%d\n",(x-tot+md)%md);
}