http://blog.youkuaiyun.com/niushuai666/article/details/7002823
这篇博客讲得不错
主要是要能理解这个trie图
有3个重要的指针,分别为p, p->fail, temp。
1.指针p,指向当前匹配的字符。若p指向root,表示当前匹配的字符序列为空。(root是Trie入口,没有实际含义)。
2.指针p->fail,p的失败指针,指向与字符p相同的结点,若没有,则指向root。
3.指针temp,测试指针 ,在建立fail指针时有寻找与p字符匹配的结点的作用。
详见代码注释
#include<bits/stdc++.h>
using namespace std;
const int N=10005;
const int maxlen=1000132;
const int maxn=10005*50;
int trie[maxn][26];
int fail[maxn];
int tag[maxn];
int sz;
queue<int >Q;
struct Aho
{
int root=0;
int newnode()//静态创建新节点
{
memset(trie[sz],-1,sizeof trie[sz]);
tag[sz]=0;
sz++;
return sz-1;
}
void init()//初始化
{
sz=0;
newnode();
}
void insert(char s[]) //插入字符串构建ac自动机,构建trie树
{
int len=strlen(s),p=0;;
for (int i=0; i<len; i++)
{
int id=s[i]-'a';
if (trie[p][id]==-1)
trie[p][id]=newnode();
p=trie[p][id];
}
tag[p]++; //结束标记
}
void getfail() //构建自动机fail指针
{
while(!Q.empty()) Q.pop();
fail[root]=root; //root指向root
for (int i=0; i<26; i++)
{
if (trie[root][i]==-1)//第一个字符不存在,指向root
trie[root][i]=root;
else //第一个字符的fail指针指向root
{
fail[trie[root][i]]=root;
Q.push(trie[root][i]); //并放入队列,待bfs扩展
}
}
while(!Q.empty())
{
int u=Q.front(); //取扩展节点
Q.pop();
for (int i=0; i<26; i++)//遍历所有子节点
{
if (trie[u][i]==-1)//如果不存在,则子节点直接指向fail[u]节点的对应子节点
trie[u][i]=trie[fail[u]][i];
else //如果存在,则该节点的fail指针指向fail[u]节点对应的子节点
{
fail[trie[u][i]]=trie[fail[u]][i];
Q.push(trie[u][i]); //继续扩展
}
}
}
}
} aho;
char ss[55];
char s[maxlen];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
aho.init();
int n;
cin>>n;
for (int i=0; i<n; i++)
{
scanf("%s",ss);
aho.insert(ss);
}
aho.getfail();
scanf("%s",s);
int len=strlen(s);
int p=aho.root;
int ans=0;
for (int i=0; i<len; i++)
{
int idx=s[i]-'a';
p=trie[p][idx];
int tmp=p; //取出当前字符在自动机上对应的节点
while(tmp!=aho.root)//如果有fail节点则一直往上跳,直到跳回root表示没有了fail后继
{
ans+=tag[tmp];//累计答案
tag[tmp]=0;//清空tag标记
tmp=fail[tmp];//跳到fail后继节点
}
}
printf("%d\n",ans);
}
return 0;
}