AC自动机模板

引用自  kuangbin大神的博客  http://www.cnblogs.com/kuangbin/p/3164106.html

写了一点自己理解的注释 方便自己查看

HDU(2222)

//======================
// HDU 2222
// 求目标串中出现了几个模式串
//====================
#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string.h>
#include <queue>
using namespace std;

struct Trie
{
    int Next[500010][26];//26是这里讨论26个小写字母的情况,根据情况修改
    int fail[500010],end[500010];//end数组表示以该节点结尾的字符串的数量
    int root,L;//L用来标记节点序号,以广度优先展开的字典树的序号
    int newnode()  //建立新节点
    {
        for(int i = 0;i < 26;i++)
            Next[L][i] = -1;     //将该节点的后继节点域初始化
        end[L++] = 0;
        return L-1;    //返回当前节点编号
    }
    void init() //初始化操作
    {
        L = 0;
        root = newnode();
    }
    void insert(char buf[])
    {
        int len = strlen(buf);
        int now = root;
        for(int i = 0;i < len;i++)
        {
            if(Next[now][buf[i]-'a'] == -1)  //如果未建立当前的后继节点,建立新的节点
                Next[now][buf[i]-'a'] = newnode();
            now = Next[now][buf[i]-'a'];
        }
        end[now]++;//以该节点结尾的字符串数量增加1
    }
    void build()
    {
        queue<int>Q; //用广度优先的方式,将树层层展开
        fail[root] = root;
        for(int i = 0;i < 26;i++)
            if(Next[root][i] == -1)
                Next[root][i] = root;
            else
            {
                fail[Next[root][i]] = root;
                Q.push(Next[root][i]);
            }
        while( !Q.empty() )
        {
            int now = Q.front();
            Q.pop();
            for(int i = 0;i < 26;i++)
                if(Next[now][i] == -1)
                    Next[now][i] = Next[fail[now]][i];//该段的最后一个节点匹配后,跳到拥有最大公共后缀的fail节点继续匹配
                else
                {
                    fail[Next[now][i]]=Next[fail[now]][i];//当前节点的fail节点等于它前驱节点的fail节点的后继节点
                    Q.push(Next[now][i]);
                }
        }
    }
    int query(char buf[])
    {
        int len = strlen(buf);
        int now = root;
        int res = 0;
        for(int i = 0;i < len;i++)
        {
            now = Next[now][buf[i]-'a'];
            int temp = now;
            while( temp != root )
            {
                res += end[temp];//加上以当前节点结尾的字符串数
                end[temp] = 0;//该题是防止计算重复的字符串
                temp = fail[temp];//每次找最大公共后缀对应的fail节点
            }
        }
        return res;
    }
    void debug()
    {
        for(int i = 0;i < L;i++)
        {
            printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],end[i]);
            for(int j = 0;j < 26;j++)
                printf("%2d",Next[i][j]);
            printf("]\n");
        }
    }
};
char buf[1000010];
Trie ac;
int main()
{
    int  t,n,ans;
    scanf("%d",&t);
    while(t--)
    {
        ac.init();
       scanf("%d",&n);
       for(int i=0;i<n;i++)
       {
           scanf("%s",buf);
           ac.insert(buf);
       }
       ac.build();
         scanf("%s",buf);
         printf("%d\n",ac.query(buf));
    }
    return 0;
}

 

posted @ 2017-09-18 23:29  hinata_hajime  阅读(142)  评论(0编辑  收藏  举报