Files
python/TangDou/AcWing/AcAutomation/1282_Study.cpp
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

93 lines
2.8 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <bits/stdc++.h>
using namespace std;
const int N = 55 * 1e4 + 10; // n个单词
const int M = 1e6 + 10; // 长度为m的文章
int n;
int tr[N][26], cnt[N], idx; // Trie树,每个结点的结束标识数组cnt,结点号计数器idx,二维代表最多可能有26条不同走向的边
char s[M]; // 字符串数组
int q[N], ne[N]; // AC自动机构建需要的队列和失配指针
// Trie树构建
void insert(char *s) {
int p = 0;
for (int i = 0; s[i]; i++) {
int t = s[i] - 'a';
if (!tr[p][t]) tr[p][t] = ++idx;
p = tr[p][t];
}
cnt[p]++;
}
// 构建AC自动机
void bfs() {
int hh = 0, tt = -1;
// 树根和树根下一层的失配边都连到树根上,所以从树根下一层开始bfs
for (int i = 0; i < 26; i++)
if (tr[0][i]) q[++tt] = tr[0][i];
while (hh <= tt) {
int p = q[hh++];
for (int i = 0; i < 26; i++) { // p节点有哪些出边
int &c = tr[p][i]; // c是p通过i这条边到达的子节点
if (c) { // 如果c点存在
// 准备填充ne[c],等于啥呢?
// 就是说如果在c后面的字符发生失配时,才会想到它爸爸c的ne[c]值,就是以p为出发点,一路向上递归,找出tmp点下是不是有i这条边
// 最终的tmp点,就是ne[c]的值
int j = ne[p];
while (j && !tr[j][i]) j = ne[j];
if (tr[j][i]) ne[c] = tr[j][i];
// 将节点c放入队列
q[++tt] = c;
}
}
// 前提: 遍历完第i-1层时,会求出第i层节点的ne值(可不一定都在i-1层啊);也就是说遍历到第i层的时候第i层的ne值已知。
}
}
int query(char *s) {
int res = 0;
int j = 0; // 从root=0开始对AC自动机进行查找
for (int i = 0; s[i]; i++) {
int t = s[i] - 'a';
while (j && !tr[j][t]) j = ne[j]; // 一直跳到有t这条边的节点处或者跳到root停止
if (tr[j][t]) j = tr[j][t];
// 累加计数
int p = j;
while (p && ~cnt[p]) {
res += cnt[p];
cnt[p] = -1; // 取消标识
p = ne[p];
}
}
return res;
}
int main() {
int T;
cin >> T;
while (T--) {
memset(tr, 0, sizeof tr);
memset(cnt, 0, sizeof cnt);
memset(ne, 0, sizeof ne);
idx = 0;
// Trie树
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> s;
insert(s);
}
// ac自动机
bfs();
// 查询
cin >> s;
printf("%d\n", query(s));
}
return 0;
}