BZOJ 1030: [JSOI2007]文本生成器( AC自动机 + dp )
之前一直没调出来T^T...早上刷牙时无意中就想出错在哪里了...
对全部单词建AC自动机, 然后在自动机上跑dp, dp(i, j)表示匹配到了第i个字符, 在自动机上的j结点的方案数, 然后枚举A~Z进行转移.
--------------------------------------------------------------------------
#include<bits/stdc++.h>
using namespace std;
#define idx(c) ((c) - 'A')
const int maxn = 109;
const int maxm = 69;
const int N = 26;
const int MOD = 10007;
struct Node {
Node *ch[26], *fail;
int id;
bool F;
} pool[maxn * maxm], *pt = pool, *root = pt++;
int CNT = 0, c = 0, p = 1, tot = 1;
int n, m, dp[2][maxn * maxm];
char s[maxn];
inline Node* newNode() {
pt->fail = root;
pt->F = false;
pt->id = CNT++;
return pt++;
}
void insert(char* c, int len) {
Node* t = root;
for(; len--; c++) {
if(!t->ch[idx(*c)]) t->ch[idx(*c)] = newNode();
t = t->ch[idx(*c)];
}
t->F = true;
}
queue<Node*> Q;
void BFS() {
for(int i = 0; i < N; i++)
if(root->ch[i]) Q.push(root->ch[i]);
while(!Q.empty()) {
Node* t = Q.front(); Q.pop();
for(int i = 0; i < N; i++) if(t->ch[i]) {
Node* o = t->fail;
while(o != root && !o->ch[i]) o = o->fail;
t->ch[i]->fail = (o->ch[i] ? o->ch[i] : root);
if(t->ch[i]->fail->F) t->ch[i]->F = true;
Q.push(t->ch[i]);
}
}
}
void DFS(Node* t) {
// if(t->F || !dp[p][t->id]) return; it's wrong
if(t->F) return;
for(int i = 0; i < N; i++) if(t->ch[i]) {
DFS(t->ch[i]);
dp[c][t->ch[i]->id] += dp[p][t->id];
if(dp[c][t->ch[i]->id] >= MOD) dp[c][t->ch[i]->id] -= MOD;
} else {
Node* o = t->fail;
while(o != root && !o->ch[i]) o = o->fail;
int id = (o->ch[i] ? o->ch[i]->id : 0);
dp[c][id] += dp[p][t->id];
if(dp[c][id] >= MOD) dp[c][id] -= MOD;
}
}
void dfs(Node* t) {
if(!t->F) {
tot -= dp[c][t->id];
if(tot < 0) tot += MOD;
}
for(int i = 0; i < N; i++)
if(t->ch[i]) dfs(t->ch[i]);
}
int main() {
root = newNode(); root->fail = root;
scanf("%d%d", &n, &m);
for(int i = 0; i < n; i++) {
scanf("%s", s);
insert(s, strlen(s));
}
BFS();
memset(dp, 0, sizeof dp);
dp[c][0] = 1;
for(int i = 0; i < m; i++) {
(tot *= 26) %= MOD;
swap(c, p);
memset(dp[c], 0, sizeof dp[c]);
DFS(root);
}
dfs(root);
printf("%d\n", tot);
return 0;
}
--------------------------------------------------------------------------
1030: [JSOI2007]文本生成器
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 2679 Solved: 1107
[Submit][Status][Discuss]
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