AK-dream

【题目描述】

JSOI 交给队员 ZYX 一个任务,编制一个称之为“文本生成器”的电脑软件:该软件的使用者是一些低幼人群,他们现在使用的是 GW 文本生成器 v6 版。

该软件可以随机生成一些文章——总是生成一篇长度固定且完全随机的文章。 也就是说,生成的文章中每个字符都是完全随机的。如果一篇文章中至少包含使用者们了解的一个单词,那么我们说这篇文章是可读的(我们称文章 $t$ 包含单词 $t$,当且仅当单词 $t$ 是文章 $s$ 的子串)。但是,即使按照这样的标准,使用者现在使用的 GW 文本生成器 v6 版所生成的文章也是几乎完全不可读的。ZYX 需要指出 GW 文本生成器 v6 生成的所有文本中,可读文本的数量,以便能够成功获得 v7 更新版。你能帮助他吗?

答案对 $10^4 + 7$ 取模。

【输入格式】

第一行有两个整数,分别表示使用者了解的单词总数 $n$ 和生成的文章长度 $m$。

接下来 $n$ 行,每行一个字符串 $s_i$,表示一个使用者了解的单词。

【输出格式】

输出一行一个整数表示答案对 $10^4 + 7$ 取模的结果。

题解

首先 很容易想到 答案就是总的方案数,也就是$26^m$减去不合法的方案数

不合法的方案数我们可以通过在AC自动机上面DP来求得

先对于那$n$个串建好AC自动机

显然,一个不合法的串在AC自动机上匹配时一定不会经过任何结束节点或者fail链上有结束节点的点

所以我们先预处理哪些是这样的节点 这个是可以在求fail的时候顺便处理的

然后设$dp[x][j]$表示现在在AC自动机上节点$x$处,串长度为$j$的方案数

转移方程:$dp[ch[x][c]][j+1]~ += ~dp[x][j]$ ($ch[x][c]$不为结束节点),初始状态$dp[0][0]=1$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <bits/stdc++.h>
using namespace std;

inline int read() {
int x = 0, f = 1; char ch = getchar();
for (; ch > '9' || ch < '0'; ch = getchar()) if (ch == '-') f = -1;
for (; ch <= '9' && ch >= '0'; ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ '0');
return x * f;
}

const int mod = 10007;
int n, m, dp[10005][205], ans;
char s[205];

struct AC_automaton{
int fail[10005], ch[10005][30], tot;
bool tag[10005];

inline void insert(char *str, int len) {
int x = 0;
for (int i = 1; i <= len; i++) {
if (!ch[x][str[i]-'A']) ch[x][str[i]-'A'] = ++tot;
x = ch[x][str[i]-'A'];
}
tag[x] = 1;
}

inline void getfail() {
queue<int> q;
for (int i = 0; i < 26; i++) if (ch[0][i]) q.push(ch[0][i]);
while (!q.empty()) {
int x = q.front(); q.pop();
tag[x] |= tag[fail[x]];
for (int i = 0; i < 26; i++) {
if (ch[x][i]) fail[ch[x][i]] = ch[fail[x]][i], q.push(ch[x][i]);
else ch[x][i] = ch[fail[x]][i];
}
}
}

inline void DP() {
memset(dp, 0, sizeof(dp));
dp[0][0] = 1;
for (int i = 0; i < m; i++) {
for (int x = 0; x <= tot; x++) {
for (int c = 0; c < 26; c++) {
if (!tag[ch[x][c]]) {
dp[ch[x][c]][i+1] = (dp[ch[x][c]][i+1] + dp[x][i]) % mod;
}
}
}
}
ans = 1;
for (int i = 1; i <= m; i++) ans = ans * 26 % mod;
for (int x = 0; x <= tot; x++) {
ans = (ans - dp[x][m] + mod) % mod;
}
}
} T;

int main() {
n = read(), m = read();
for (int i = 1; i <= n; i++) {
scanf("%s", s + 1);
T.insert(s, strlen(s + 1));
}
T.getfail();
T.DP();
printf("%d\n", ans);
return 0;
}

 评论