HDU 2222 (AC自动机)

来源:互联网 发布:淘宝客推广宣传海报 编辑:程序博客网 时间:2024/05/29 09:03

http://acm.hdu.edu.cn/showproblem.php?pid=2222

AC自动机模板题

数组版本(参考kuangbin和昀神)写的精简巧妙,很喜欢.

(找失败指针中,原来node[i][j]表示字典树i节点的字符为j的儿子的编号。

现在扩展一下,如果i没有字符j这个儿子那么node[i][j]就是i节点沿着失败指针一直走到有字符为j的儿子的节点的编号,若不存在,指向根。相当于一个路径压缩的思想)

Code:

#include <cstdio>#include <cstring>#include <queue>using namespace std;const int maxn =1000010;struct Trie{    int node[maxn][26],fail[maxn],num[maxn],root,L;    int newnode()    {        for(int i=0;i<26;i++) node[L][i]=-1;        num[L]=0;        return L++;    }    void init(){L=0,root=newnode();}    void insert_fun(char buf[])    {        int now=root;        int len=strlen(buf);        for(int i=0;i<len;i++)        {            if(node[now][buf[i]-'a']==-1)                node[now][buf[i]-'a']=newnode();            now=node[now][buf[i]-'a'];        }        num[now]++;    }    void build()    {        queue<int> q;        fail[root]=root;        for(int i=0;i<26;i++)            if(node[root][i]==-1)                node[root][i]=root;            else            {                fail[node[root][i]]=root;                q.push(node[root][i]);            }        while(!q.empty())        {            int now=q.front();            q.pop();            for(int i=0;i<26;i++)            {                if(node[now][i]==-1)                    node[now][i]=node[fail[now]][i];  //将该边指向当前节点fail指针指向的相应字符连接的节点                else                {   //将儿子节点的fail指针指向当前节点fail指针指向相应字符接的节点                    fail[node[now][i]]=node[fail[now]][i];                    q.push(node[now][i]);                }            }        }    }    int query(char buf[])    {        int len=strlen(buf);        int now=root,res=0;        for(int i=0;i<len;i++)        {            now=node[now][buf[i]-'a'];            int temp=now;            while(temp!=root&& num[temp]!=-1)            {                res+=num[temp];                num[temp]=-1;                temp=fail[temp];            }        }        return res;    }};Trie ac;char buf[maxn];int main(){    int T,n;    scanf("%d",&T);    while(T--)    {        scanf("%d",&n);        ac.init();        for(int i=0;i<n;i++)        {            scanf("%s",buf);            ac.insert_fun(buf);        }        ac.build();        scanf("%s",buf);        int ans=ac.query(buf);        printf("%d\n",ans);    }    return 0;}



以前学习用的指针版本,留个纪念(曾经再做某一题时,不写释放内存,超内存,写释放内存就超时,所以还是推荐上面的版本) 。

#include <cstdio>#include <iostream>#include <cstring>using namespace std;#define T_SIZE 1000000#define P_SIZE 50#define TOTAL_P 10000struct trie{ //利用结构体来封装字典树的节点    trie* next[26];    trie* fail;    int num;    trie()    {        for(int i=0;i<26;i++)            next[i]=NULL;        fail=NULL;        num=0;    }};char T[T_SIZE+1];char P[P_SIZE+1];trie *q[TOTAL_P*P_SIZE];void insert(trie* root,char* s){    trie *p=root;    for(int i=0;s[i]!='\0';i++)    {        if(p->next[s[i]-'a']==NULL)            p->next[s[i]-'a']=new trie;        p=p->next[s[i]-'a'];    }    p->num++;}void build_ac_automation(trie* root)//利用广搜构建失败指针{    int head=0,tail=0;    q[tail++]=root;    while(head!=tail)    {        trie *front=q[head++];        //front为队头        for(int i=0;i<26;i++)            if(front->next[i]!=NULL)                //遍历队头元素的子节点            {                trie* p=front->fail;                while(p!=NULL)                //只有根节点的失败指针为NULL                {                    if(p->next[i]!=NULL)                    {                        front->next[i]->fail=p->next[i];                        break;                    }                    p=p->fail;                }                if(p==NULL)                front->next[i]->fail=root;                q[tail++]=front->next[i];            }    }}int ac_find(trie* root,char* T){    trie* p =root;    int sum=0;    for(int i=0,len=strlen(T);i<len;i++)    {            while(p->next[T[i]-'a']==NULL&& p!=root)        {            p=p->fail;        }            if(p->next[T[i]-'a']!=NULL)            p=p->next[T[i]-'a'];        trie* temp =p;        while(temp!=root && temp->num!=-1)        {            sum+=temp->num;            temp->num=-1;            temp=temp->fail;        }    }    return sum;}int main(){    int t;    for(scanf("%d",&t);t>0;t--)    {        trie *root =new trie;        int n;        scanf("%d",&n);        getchar();        for(int i=0;i<n;i++)        {            gets(P);            insert(root,P);        }        build_ac_automation(root);        gets(T);        printf("%d\n",ac_find(root,T));    }    return 0;}