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的值。
Sample Input
2 2
A
B
A
B
Sample Output
100
HINT
Source
AC自动机+dp。
其实很容易想到答案就是26^m-∑(不包含单词且长度为m的串),我们可以考虑用dp来求后边这部分。
设dp[i][j]表示串长为i走到AC自动机的j节点时的方案数,显然一个节点可以向它的儿子转移。
有一个应该注意的地方是,一个单词不一定只有到结尾时才能判断是否可行,可能他中间某一段已经包含一个单词了。
所以如果一个点fai指针指向的点是单词的结尾,这个点同样不能被访问。
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<queue> 4 #include<cstring> 5 #define mod 10007 6 #define M 10001 7 using namespace std; 8 int n,m,ans,sum,cnt; 9 int fail[M],ch[M][26],dp[101][M]; 10 bool end[M]; 11 char s[M]; 12 void insert(string s) 13 { 14 int len=s.length(),now=0; 15 for(int i=0;i<len;i++) 16 { 17 if(!ch[now][s[i]-'A']) ch[now][s[i]-'A']=++cnt; 18 now=ch[now][s[i]-'A']; 19 } 20 end[now]=true; 21 } 22 void build_fail() 23 { 24 queue<int>q; 25 for(int i=0;i<26;i++) 26 if(ch[0][i]) 27 { 28 fail[ch[0][i]]=0; 29 q.push(ch[0][i]); 30 } 31 while(!q.empty()) 32 { 33 int u=q.front(); q.pop(); 34 for(int i=0;i<26;i++) 35 { 36 if(ch[u][i]) 37 { 38 end[ch[u][i]]|=end[ch[fail[u]][i]]; 39 fail[ch[u][i]]=ch[fail[u]][i]; 40 q.push(ch[u][i]); 41 } 42 else ch[u][i]=ch[fail[u]][i]; 43 } 44 } 45 } 46 int main() 47 { 48 scanf("%d%d",&n,&m); 49 for(int i=1;i<=n;i++) 50 { 51 scanf("%s",s); 52 insert(s); 53 } 54 build_fail(); 55 dp[0][0]=1; 56 for(int i=1;i<=m;i++) 57 for(int j=0;j<=cnt;j++) 58 for(int k=0;k<26;k++) 59 if(!end[ch[j][k]]) 60 dp[i][ch[j][k]]=(dp[i][ch[j][k]]+dp[i-1][j])%mod; 61 sum=1; 62 for(int i=1;i<=m;i++) sum=sum*26%mod; 63 for(int i=0;i<=cnt;i++) ans=(ans+dp[m][i])%mod; 64 printf("%d",(sum-ans+mod)%mod); 65 return 0; 66 }