BZOJ1030: [JSOI2007]文本生成器(AC自動機)
阿新 • • 發佈:2018-07-01
tro 文件 編制 das discus def tor sca else Time Limit: 1 Sec Memory Limit: 162 MB
Submit: 5984 Solved: 2523
[Submit][Status][Discuss]
標準,使用者現在使用的GW文本生成器v6版所生成的文章也是幾乎完全不可讀的?。ZYX需要指出GW文本生成器 v6
生成的所有文本中可讀文本的數量,以便能夠成功獲得v7更新版。你能幫助他嗎?
A
B
Submit: 5984 Solved: 2523
[Submit][Status][Discuss]
Description
JSOI交給隊員ZYX一個任務,編制一個稱之為“文本生成器”的電腦軟件:該軟件的使用者是一些低幼人群,
他們現在使用的是GW文本生成器v6版。該軟件可以隨機生成一些文章―――總是生成一篇長度固定且完全隨機的文
章—— 也就是說,生成的文章中每個字節都是完全隨機的。如果一篇文章中至少包含使用者們了解的一個單詞,
那麽我們說這篇文章是可讀的(我們稱文章a包含單詞b,當且僅當單詞b是文章a的子串)。但是,即使按照這樣的
生成的所有文本中可讀文本的數量,以便能夠成功獲得v7更新版。你能幫助他嗎?
Input
輸入文件的第一行包含兩個正整數,分別是使用者了解的單詞總數N (<= 60),GW文本生成器 v6生成的文本固
定長度M;以下N行,每一行包含一個使用者了解的單詞。這裏所有單詞及文本的長度不會超過100,並且只可能包
含英文大寫字母A..Z
Output
一個整數,表示可能的文章總數。只需要知道結果模10007的值。
Sample Input
2 2A
Sample Output
100HINT
Source
感覺自己好菜啊這種題都想不出來qwq。。
剛開始想的是容斥—>把所有合法的統計出來再減去重復的!!沒錯我就這麽xjb容斥了半個小時qwq。。
看了一下大佬的題解瞬間思路明了。。。
考慮經典補集轉化思想:這題問的是可讀的,我們可以轉化為總的-不可讀的。
總的就是$26^m$
不可讀的考慮DP。設$f[i][j]$表示長度為$i$時,在自動機上第$j$個位置有多少不可讀的情況
開始時$f[0][0] = 1$,轉移的時候枚舉一下出邊
註意!!如果一個點的$fail$指針指向的位置不可讀,那麽該節點也是不可讀的!
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<queue> #include<cmath> using namespace std; const int MAXN = 1e5 + 10, B = 26, mod = 10007; inline int read() { char c = getchar(); int x = 0, f = 1; while(c < ‘0‘ || c > ‘9‘) {if(c == ‘-‘) f = -1; c = getchar();} while(c >= ‘0‘ && c <= ‘9‘) x = x * 10 + c - ‘0‘, c = getchar(); return x * f; } int N, M; int ch[MAXN][26], fail[MAXN], tot = 0, root = 0, val[MAXN]; char s[101]; void insert(char *s) { int N = strlen(s + 1), now = root; for(int i = 1; i <= N; i++) { int x = s[i] - ‘A‘; if(!ch[now][x]) ch[now][x] = ++tot; now = ch[now][x]; } val[now] = 1;//不能經過 } void GetFail() { queue<int> q; for(int i = 0; i < B; i++) if(ch[root][i]) q.push(ch[root][i]); while(!q.empty()) { int p = q.front(); q.pop(); for(int i = 0; i < B; i++) if(ch[p][i]) fail[ch[p][i]] = ch[fail[p]][i], q.push(ch[p][i]); else ch[p][i] = ch[fail[p]][i]; val[p] |= val[fail[p]]; } } int f[101][MAXN]; // 當前長度為i,在自動機的地j號節點 int solve() { f[0][0] = 1; for(int i = 1; i <= M; i++) { for(int j = 0; j <= tot; j++) { for(int k = 0; k < B; k++) {//枚舉出邊 int son = ch[j][k]; if(val[son]) continue; f[i][son] = (f[i][son] % mod+ f[i - 1][j] % mod) % mod; } } } int ans = 0; for(int i = 0; i <= tot; i++) ans = (ans + f[M][i] % mod) % mod; int sum = 1; for(int i = 1; i <= M; i++) sum = (26 * sum) % mod; return (sum - ans + mod) % mod; } main() { #ifdef WIN32 freopen("a.in", "r", stdin); #endif scanf("%d %d", &N, &M); for(int i = 1; i <= N; i++) scanf("%s", s + 1), insert(s); GetFail(); printf("%d", solve() % mod); }
BZOJ1030: [JSOI2007]文本生成器(AC自動機)