预备知识:
AC自动机数据结构参考了Trie树(字典树),在字符匹配上参考了KMP算法,所以在学习AC自动机之前应学习KMP算法及Trie树。
建议先看一道模板题再看博客
HDU2222
参考博客:
AC自动机 算法详解(图解)附带模板
例题
[kuangbin带你飞]专题十七 AC自动机
HDU2222 模板题
#include <queue>
#include <cstdlib>
#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int mn = 5e5+7;
int tree[mn][26]; //字典树
int flagg[mn]; //记录该单词出现次数
int fail[mn]; //失败时的回溯指针
int tot = 0;
void insert_(char s[]){
int root = 0,len=strlen(s);
for(int i=0;i<len;i++){
int next = s[i] - 'a';
if(!tree[root][next])
tree[root][next] = ++tot;
root = tree[root][next];
}
flagg[root]++; //当前节点单词数+1
}
void getFail(){
queue <int>q;
for(int i=0;i<26;i++){ //将第一层所有出现了的字母扔进队列
if(tree[0][i]){
fail[tree[0][i]] = 0; //第一层指向第零层(根)
q.push(tree[0][i]);
}
}
//fail[now] ->当前节点now的失败指针指向的地方,若没有能指向的地方则为0
while(!q.empty()){
int now = q.front();
q.pop();
for(int i=0;i<26;i++){ //查询26个字母
if(tree[now][i]){
fail[tree[now][i]] = tree[fail[now]][i];
q.push(tree[now][i]);
}else{
tree[now][i] = tree[fail[now]][i];
}
}
}
}
int find_(char s[]){
int now = 0,ans = 0,len=strlen(s);
for(int i=0;i<len;i++){ //遍历文本串
now = tree[now][s[i]-'a']; //从s[i]点开始寻找
for(int j=now;j && flagg[j]!=-1;j=fail[j]){
//一直向下寻找,直到匹配失败(失败指针指向根或者当前节点已找过).
ans += flagg[j];
flagg[j] = -1; //将遍历国后的节点标记,防止重复计算
}
}
return ans;
}
void init()
{
for(int i=0;i<=tot;i++)
{
flagg[i]=0;
fail[i]=0;
for(int j=0;j<26;j++)
{
tree[i][j]=0;
}
}
tot=0;
}
char str[mn*2];
int main()
{
ios::sync_with_stdio(false);
int t;
scanf("%d",&t);
while(t--)
{
init();
int n;
scanf("%d",&n);
string s;
while(n--){
scanf("%s",str);
insert_(str);
}
getFail();
scanf("%s",str);
int res=find_(str);
cout<<res<<endl;
}
return 0;
}