ac自动机
Chivas-Regal
#
# 洛谷P4052_文本生成器
# 🔗
# 💡
正难则反,这个是让我们求含有可读文本的串数,我们可以尝试求一下不含有可读文本的串数
可读文本会在自动机插入时的所有尾节点被标记,同时含有可读文本的也可能是以被标记节点为后缀的串
故首先对自动机 部分插入时的尾部标记为 ,一个串如果后缀是可读文本那么自己也不行,故要 上自己的 指向的标记
既然是计数,就在这个自动机上
设置 表示文本长度为 ,到节点 的目标数量
在自动机上往下走并转移,即枚举长度,枚举节点 ,枚举 个子节点 ,如果 被标记那么都不转移,否则令
最后统计 中 没有被标记的数量和
用 减去它即可
# ✅
const int N = 6010;
const int mod = 1e4 + 7;
struct AcAutoMaton {
int ct, root;
int fa[N], to[N][26];
bool mark[N];
inline AcAutoMaton () {
ct = root = 1;
}
inline void insert (string s) {
int p = root;
for (char c : s) {
if (!to[p][c - 'A']) to[p][c - 'A'] = ++ct;
p = to[p][c - 'A'];
}
mark[p] = true;
}
inline void build (vector<string> v) {
for (string it : v) insert(it);
for (int i = 0; i < 26; i ++) to[0][i] = root;
queue<int> que; que.push(root);
while (!que.empty()) {
int u = que.front(); que.pop();
for (int i = 0; i < 26; i ++) {
if (to[u][i]) {
fa[to[u][i]] = to[fa[u]][i];
mark[to[u][i]] |= mark[fa[to[u][i]]];
que.push(to[u][i]);
} else {
to[u][i] = to[fa[u]][i];
}
}
}
}
} acam;
int dp[110][N];
int main () {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m; cin >> n >> m;
vector<string> v(n); for (string &it : v) cin >> it;
acam.build(v);
dp[0][1] = 1;
for (int i = 1; i <= m; i ++) {
for (int j = 1; j <= acam.ct; j ++) {
for (int c = 0; c < 26; c ++) {
if (!acam.mark[j] && !acam.mark[acam.to[j][c]]) {
(dp[i][acam.to[j][c]] += dp[i - 1][j]) %= mod;
}
}
}
}
int res = 1;
for (int i = 0; i < m; i ++) res = res * 26 % mod;
for (int i = 1; i <= acam.ct; i ++) {
(res -= dp[m][i]) %= mod;
}
cout << (res % mod + mod) % mod << endl;
}
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
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