hdu2222 Keywords Search (AC自动机)

来源:互联网 发布:少数民族 知乎 编辑:程序博客网 时间:2024/06/13 18:45

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 26struct 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;}
0 0