学习1:小蒟蒻yyb的博客
学习2:大佬的板子
AC自动机:求多个模式串在文本串中出现的次数
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
const int INF = 0x3f3f3f3f;
int n, m, k;
namespace AC {//AC自动机板子
struct Tree {//字典树
int fail;//失配指针
int vis[26];//子节点位置
int end;//标记有几个单词以这个节点
} AC[N];//trie树 根节点为0
int cnt = 0;//编号
void clear(int x){//清空
memset(AC[x].vis, 0, sizeof(AC[x].vis));
AC[x].fail = 0;//结束标志 一般以0为root
AC[x].end = 0;
}
void build(string s) {
int len = s.length();
int now = 0;//字典树当前指针
for (int i = 0; i < len; ++i) {
if (AC[now].vis[s[i] - 'a'] == 0) {
//trie树没有这个子节点
AC[now].vis[s[i] - 'a'] = ++cnt;
clear(cnt);
}
now = AC[now].vis[s[i] - 'a'];
}
AC[now].end++;//标记单词结尾
}
queue<int> q;
void get_fail() {//构造fail指针
for (int i = 0; i < 26; ++i) {//第二层提前预处理
if (AC[0].vis[i]) {
AC[AC[0].vis[i]].fail = 0;//指向根节点
q.push(AC[0].vis[i]);
}
}
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = 0; i < 26; ++i) {
if (AC[u].vis[i]) {//存在子节点
AC[AC[u].vis[i]].fail = AC[AC[u].fail].vis[i];
//子节点的fail指针 指向父节点fail指针所指向的节点 的相同的子节点 vis[i]相同
//如果那个子节点不存在 相当于该子节点fail=0 指向了根节点
q.push(AC[u].vis[i]);
} else {
//不存在子节点
AC[u].vis[i] = AC[AC[u].fail].vis[i];
//当前节点的子节点 指向 当前节点fail指针指向的节点的子节点
//把当前节点fail指向的节点 的子节点 作为自己的子节点
}
}
}
}
int AC_Query(string s) {//这里的s是被匹配的文本串
int len = s.length();
int now = 0, ans = 0;
for (int i = 0; i < len; ++i) {
now = AC[now].vis[s[i] - 'a'];
for (int j = now; j && AC[j].end != -1; j = AC[j].fail) {
//j不能延伸到根节点 且AC[j]不能是走过的
ans += AC[j].end;
AC[j].end = -1;
}
}
return ans;
}
void init() {
cnt = 0;
clear(0);
}
}
using namespace AC;
string s;
int main() {
ios::sync_with_stdio(0);
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> s;
build(s);
}
get_fail();//求出失配指针
cin >> s;
cout << AC_Query(s) << endl;
return 0;
}