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

89 lines
3.4 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;
const int M = 1e6 + 10;
int n;
int tr[N][26], cnt[N], idx;
char s[M];
int q[N], ne[N];
// 标准的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]++;
}
// 构建失配指针
void bfs() {
int hh = 0, tt = -1;
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++) {
int &c = tr[p][i]; // p:父节点,c:子节点,&:引用,可以向c赋值,等同于向tr[p][i]赋值
if (c) { // 如果点c存在
ne[c] = tr[ne[p]][i]; // 为点c填充失配数组ne,当点c失配时,跳到它父亲记录好的tr[ne[p]][i]位置上去,而它的父亲对应值,是记录了祖先传递下来的,不用再回溯求递归求解
q[++tt] = c; // 入队列,为后续填充做准备
} else
c = tr[ne[p]][i]; // 如果不存在,这个位置需要不需进行记录值呢?如果不用的话,那么后面再有指望它来提供信息的,就狒狒了,既然要递推,就要保证数据的完整
// 那怎么办呢?答案就是也依赖于它的真系血亲进行数据传递,说白了,就是自己这不匹配了,那么需要去哪里匹配呢?还不想用while向上递归,那就需要向下传递时记录清楚呗。
// 这个真系血亲是和c点拥有最长公共前后缀的节点,跳到它那里去
}
}
// ① 遍历完第i-1层时,会求出第i层节点的ne值(可不一定都在i-1层啊);也就是说遍历到第i层的时候第i层的ne值已知。
}
// 跑一下文本串
int query(char *s) {
int res = 0;
int j = 0; // 在Trie中游走的指针j, 从根开始对AC自动机进行查找
for (int i = 0; s[i]; i++) { // 枚举文本串每个字符
int t = s[i] - 'a'; // 字符映射的边序号
while (j && !tr[j][t]) j = ne[j]; // 如果没有回退到根,并且,当前游标位置没有t这条边,继续利用失配指针回退
if (tr[j][t]) j = tr[j][t]; // 如果命中,停下来,找到匹配的失配节点
// 统计在当前失配节点向根游走,有多少个完整的模式串被命中
int p = j;
while (p && ~cnt[p]) {
res += cnt[p]; // 记录个数
cnt[p] = -1; // 取消标识,一个模式串就算命中多次,也计数为1。测试用例中有重复的模式串,所以cnt[p]可能大于1
p = ne[p]; // 不断向上回退
}
}
return res;
}
int main() {
int T;
cin >> T;
while (T--) {
// 多组测试数组,初始化AC自动机
memset(tr, 0, sizeof tr);
memset(cnt, 0, sizeof cnt);
memset(ne, 0, sizeof ne);
idx = 0;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> s;
insert(s); // 构造Trie树
}
// 利用bfs构建Trie树的fail指针,也就是AC自动机
bfs();
// 输入文本串开始进行查询
cin >> s;
printf("%d\n", query(s));
}
return 0;
}