You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

93 lines
2.8 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#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;
}