[BZOJ1030]文本生成器

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

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 }

猜你喜欢

转载自www.cnblogs.com/Slrslr/p/9491500.html