核心思想简介
KMP算法一样, AC自动机在匹配时如果当前字符匹配失败,那么利用fail指针进行跳转。由此可知如果跳转,跳转到的串的前缀,必为跳转前的模式串的后缀。由此可知,跳转的新位置的深度一定小于跳之前的节点。所以我们可以利用 bfs在 Trie上面进行 fail指针的求解。
设这个节点上的字母为x,沿着他父亲的失败指针走,直到走到一个节点,他的儿子中也有字母为x的节点。然后把当前节点的失败指针指向那个字符也为x的儿子。如果一直走到了root都没找到,那就把失败指针指向root。
给两个经典的图:
计算Trie的失败指针
参考博客:http://www.cnblogs.com/xudong-bupt/p/3433506.html
题目:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=16403
code:
#include <iostream>
#include <cstdio>
#include <queue>
#include <stdlib.h>
#include <memory.h>
#define maxn 1000010
#define maxnode 500050
#define sigma_size 26
using namespace std;
char P[maxn],str[55];
int f[maxnode];
int last[maxnode],val[maxnode],has[maxnode];
int ch[maxnode][sigma_size],sz;
void init()
{
sz=1;last[0]=val[maxnode]=0;
memset(ch[0],0,sizeof ch[0]);
}
int indx(char c){return c-'a';}
void insert(char *s)
{
int u=0,n=strlen(s);
for(int i=0;i<n;i++)
{
int c = indx(s[i]);
if(!ch[u][c])
{
memset(ch[sz],0,sizeof ch[sz]);
val[sz]=0;
ch[u][c]=sz++;
}
u=ch[u][c];
}
++val[u];
}
void getfile()
{
queue<int>Q;
f[0]=0;
for(int c=0; c<sigma_size; c++){
int u = ch[0][c];
if(u){
f[u]=0;Q.push(u);last[u]=0;
}
}
while(!Q.empty()){
int r = Q.front();Q.pop();
for(int c = 0 ; c < sigma_size ; c++)
{
int u = ch[r][c];
if(!u)continue;
Q.push(u);
int v = f[r];
while(v&&!ch[v][c])v=f[v];
f[u] = ch[v][c];
last[u]=val[f[u]]?f[u]:last[f[u]];
}
}
}
void print(int t)
{
if(t){
has[t]=1;
print(last[t]);
}
}
int find(char *s)
{
memset(has,0,sizeof has);
int n=strlen(s),j=0;
for(int i = 0; i< n;i++)
{
int c=indx(s[i]);
while(j&&!ch[j][c])j=f[j];
j=ch[j][c];
if(val[j])print(j);
else print(last[j]);
}
int ans=0;
for(int i=0;i<sz;i++)if(has[i])ans+=val[i];
return ans;
}
int main()
{
// freopen("in.txt","r",stdin);
int T,n;scanf("%d",&T);
while(T--)
{
init();
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%s",str);
insert(str);
}
getfile();
scanf("%s",P);
printf("%d\n",find(P));
}
return 0;
}