参考yyb大佬写的板子QAQ
#include <iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
using namespace std;
struct Trie {
int fail;
int son[26];
int end;
} AC[1000005];
int cnt = 0;
inline void Build(string s) { //把模式串依次插入trie
int l = s.length();
int now = 0;
for(int i = 0; i < l; i++) {
if(AC[now].son[s[i] - 'a'] == 0) {
AC[now].son[s[i] - 'a'] = ++cnt;
}
now = AC[now].son[s[i] - 'a'];
}
AC[now].end++;//以now为结尾的字符串个数++
}
void getFail() { //计算失配指针
queue<int> q;
for(int i = 0; i < 26; i++) { //单独处理第二层的失配指针
if(AC[0].son[i] != 0) { //根结点的这个儿子存在的话
AC[AC[0].son[i]].fail = 0;//因为比它深度更浅的只有根结点了
q.push(AC[0].son[i]);//入队准备进行BFS
}
}
while(!q.empty()) {
int u = q.front();
q.pop();
for(int i = 0; i < 26; i++) {//找当前结点的所有子结点,因为是BFS,所以上一层所有结点的fail指针都已经计算出来了,满足计算当前结点fail指针的条件
if(AC[u].son[i] != 0) {//如果这个子结点存在的话
AC[AC[u].son[i]].fail = AC[AC[u].fail].son[i];//当前结点子结点的失配指针设置为当前结点的fail指针指向的结点的所有子结点中和当前结点子结点值相同的结点
q.push(AC[u]. son[i]);//入队
} else {
AC[u].son[i] = AC[AC[u].fail].son[i];//当前结点的子结点不存在,就把这个子结点设置为当前结点失配指针指向的结点的所有子结点中和当前结点子结点值相同的结点
//相当于走到了当前结点发现无路可走,就退而求其次,看看跳到从根到当前结点这个串的最长后缀的最后面能不能继续走下去
//注意此时当前结点fail指针指向的结点由于深度更浅,它的son[i]已经被更浅层的结点更新过了,所以可以拿过来用
//考虑这么一棵trie:0-1-2-3 0-2-3 0-3-4
}
}
}
}
int AC_Query(string s) { //对文本串进行匹配
int l = s.length();
int now = 0, ans = 0;
for(int i = 0; i < l; i++) {
now = AC[now].son[s[i] - 'a'];
for(int t = now; t && AC[t].end != -1; t = AC[t].fail) {
ans += AC[t].end;
AC[t].end = -1;
}
}
return ans;
}
int main() {
freopen("data.txt", "r", stdin);
int n;
cin >> n;
for(int i = 1; i <= n; i++) {
string s;
cin >> s;
Build(s);
}
AC[0].fail = 0;
getFail();
string t;
cin >> t;
cout << AC_Query(t) << endl;
return 0;
}