[BZOJ1030] [JSOI2007] 文本生成器 (AC自动机 & dp)
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
Solution
和$BZOJ1009$类似,这题有多个模式串,所以先建出$AC$自动机
$f[i][j]$表示长度为$i$匹配到自动机上第$j$个节点时的情况数,根据$f\!ail$指针的情况将$f[i]$转移到$f[i+1]$上
那么答案就是总文章数减去所有不含模式串的情况数,当然有可能会出现一个模式串包含另一个模式串的情况,这种情况也要排除掉
其实这个$dp$更像是在$trie$图上的转移
1 #include <bits/stdc++.h> 2 using namespace std; 3 int trie[6005][26], fail[6005], f[105][6005], ptot, q[6005]; 4 bool lst[6005]; 5 char s[105]; 6 7 void insert() 8 { 9 int now = 0; 10 for(int i = 0; s[i]; ++i) 11 { 12 if(!trie[now][s[i] - 65]) 13 trie[now][s[i] - 65] = ++ptot; 14 now = trie[now][s[i] - 65]; 15 } 16 lst[now] = true; 17 } 18 19 void getfail() 20 { 21 int now, front = 0, back = 0; 22 for(int i = 0; i < 26; ++i) 23 if(trie[0][i]) q[++back] = trie[0][i]; 24 while(front != back) 25 { 26 now = q[++front]; 27 for(int i = 0; i < 26; ++i) 28 if(trie[now][i]) 29 { 30 fail[trie[now][i]] = trie[fail[now]][i]; 31 q[++back] = trie[now][i]; 32 } 33 else trie[now][i] = trie[fail[now]][i]; 34 if(lst[fail[now]]) lst[now] = true; 35 } 36 } 37 38 int main() 39 { 40 int n, m, ans = 1, now; 41 scanf("%d%d", &n, &m); 42 for(int i = 1; i <= n; ++i) 43 { 44 scanf("%s", s); 45 insert(); 46 } 47 getfail(), f[0][0] = 1; 48 for(int i = 0; i < m; ++i) 49 for(now = 0; now <= ptot; ++now) 50 { 51 if(lst[now] || !f[i][now]) continue; 52 for(int j = 0; j < 26; ++j) 53 { 54 int k = trie[now][j]; 55 (f[i + 1][k] += f[i][now]) %= 10007; 56 } 57 } 58 for(int i = 1; i <= m; ++i) 59 ans = ans * 26 % 10007; 60 for(int i = 0; i <= ptot; ++i) 61 if(!lst[i]) ans = (ans + 10007 - f[m][i]) % 10007; 62 printf("%d\n", ans); 63 return 0; 64 }