AC自动机入门标准题。
主要总结建立fail指针过程:设这个节点上的字母为a,沿着他父亲的fail指针走,直到走到一个节点,他的儿子中也有字母为a的节点。然后把当前节点的失fail指针指向那个字母也为a的儿子。如果一直走到了root都没找到,那就把失败指针指向root。
使用广度优先搜索BFS,层次遍历节点来处理,每一个节点的失败路径。
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <queue>
#include <malloc.h>
using namespace std;
#define maxn 26
struct Trie
{
Trie *next[maxn];
Trie *fail;
int num;
Trie() /*初始化*/
{
int i;
for(i=0;i<maxn;i++)
next[i]=NULL;
fail=NULL;
num=0;
}
}*root;
void inserttrie(char *str)
{
int len=strlen(str);
Trie *p=root;
for(int i=0;i<len;i++)
{
int id=str[i]-'a';
if(p->next[id]==NULL)
{
p->next[id]=new Trie;
}
p=p->next[id];
}
p->num++; /*所有keyword的最后一个字母的num=1,为后面作计算*/
}
void buildfail() /*BFS初始化fail指针*/
{
Trie *son,*temp,*p=root;
queue<struct Trie*>que;
que.push(p);
while(!que.empty())
{
temp=que.front();
que.pop();
for(int i=0;i<maxn;i++)
{
son=temp->next[i];
if(son!=NULL)
{
if(temp==root)son->fail=root; /*第一次,即temp为根时*/
else
{
p=temp->fail;
while(p)
{
if(p->next[i])
{
son->fail=p->next[i];
break;
}
p=p->fail;
}
if(!p) son->fail=root;
}
que.push(son); /*同一层的字母按先后顺序进队*/
}
}
}
}
int querry(char *str) /*视问题不同而改变*/
{
int ans=0;
int len=strlen(str);
Trie *p,*temp;
p=root;
for(int i=0;i<len;i++)
{
int id=str[i]-'a';
while(p!=root&&!p->next[id])p=p->fail; /*不是根结点但next[id]为空了,则跳转到p的fail*/
p=p->next[id];
if(p==NULL)p=root; /*若p为空了,则这一条分支到尽头,就跳回root*/
temp=p; /*p不动,temp往后走*/
while(temp!=root) /*BFS,将所有可能性都走一遍,直到尽头就可以+1*/
{
if(temp->num>=0)
{
ans+=temp->num;
temp->num=-1;
}
else
break;
temp=temp->fail;
}
}
return ans;
}
int main()
{
int t;
char keyword[51],str[1001000];
scanf("%d",&t);
while(t--)
{
root=new Trie;
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%s",keyword);
inserttrie(keyword);
}
buildfail();
scanf("%s",str);
printf("%d\n",querry(str));
}
return 0;
}