JSOI交给队员ZYX一个任务,编制一个称之为“文本生成器”的电脑软件:该软件的使用者是一些低幼人群,他们现在使用的是GW文本生成器v6版。该软件可以随机生成一些文章―――总是生成一篇长度固定且完全随机的文章——
也就是说,生成的文章中每个字节都是完全随机的。如果一篇文章中至少包含使用者们了解的一个单词,那么我们说这篇文章是可读的(我们称文章a包含单词b,当且仅当单词b是文章a的子串)。但是,即使按照这样的标准,使用者现在使用的GW文本生成器v6版所生成的文章也是几乎完全不可读的?。
ZYX需要指出GW文本生成器 v6生成的所有文本中可读文本的数量,以便能够成功获得v7更新版。你能帮助他吗?
输入文件的第一行包含两个正整数,分别是使用者了解的单词总数N (<=
60),GW文本生成器 v6生成的文本固定长度M;以下N行,每一行包含一个使用者了解的单词。
这里所有单词及文本的长度不会超过100,并且只可能包含英文大写字母A..Z 。
一个整数,表示可能的文章总数。只需要知道结果模10007的值。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define p 10007
#define N 10010
int sz,son[N][30],fail[N],q[N],loc[N],f[110][N],n,m,ans;bool end[N];
void clear()
{
sz=1;
for (int i=1; i<=26; i++) son[0][i]=1;
}
void insert(char s[])
{
int x=1;
for (int i=0; i<strlen(s); i++)
{
if (!son[x][s[i]-‘A‘+1]) son[x][s[i]-‘A‘+1]=++sz,x=sz;
else x=son[x][s[i]-‘A‘+1];
}
end[x]=1;
}
void buildfail()
{
int head=0,tail=1; q[0]=1; fail[1]=0;
while (head<tail)
{
int now=q[head++];
for (int i=1; i<=26; i++)
{
if (!son[now][i]) continue;
int ff=fail[now];
while(!son[ff][i]) ff=fail[ff];
fail[son[now][i]]=son[ff][i];
if (end[son[ff][i]]) end[son[now][i]]=1;
q[tail++]=son[now][i];
}
}
}
int quick_pow(int x,int y)
{
long long re=1;
for (int i=y; i; i>>=1,x=x*x%p)
if (i&1) re=re*x%p;
return (int)re;
}
int main()
{
clear(); char s[N];
scanf("%d%d",&n,&m);
for (int i=1; i<=n; i++)
scanf("%s",s),insert(s);
buildfail();
f[0][1]=1;
for (int i=1; i<=m; i++)
for (int j=1; j<=sz; j++)
if (!end[j] && f[i-1][j])
for (int k=1; k<=26; k++)
{
int tmp=j;
while (!son[tmp][k]) tmp=fail[tmp];
f[i][son[tmp][k]]+=f[i-1][j];
f[i][son[tmp][k]]%=p;
}
for (int i=1; i<=sz; i++)
if (!end[i]) ans+=f[m][i],ans%=p;
int tot=quick_pow(26,m);
printf("%d\n",(tot-ans+p)%p);
return 0;
}