码迷,mamicode.com
首页 > 其他好文 > 详细

AC自动机学习笔记

时间:2015-07-15 22:20:46      阅读:109      评论:0      收藏:0      [点我收藏+]

标签:

搞了一晚上AC自动机,发现需要先学trie和kmp……都不会啊。。于是又去现学KMP和trie。。终于基本看懂AC自动机了。

#include <iostream>
using namespace std;

struct node
{
    int next[26];//每一个节点可以扩展到的字母
    int fail;//每一个节点的失配指针
    int count;//记录每一个可以构成单词的字符串距根节点的深度
    void init()//构造
    {
        memset(next, -1, sizeof(next));//初始化next为-1,即不与任何值相连
        fail = 0;//失配指针为空
        count = 0;//一开始没有单词赋为0
    }
}s[500005];

int sind;//记录节点的编号
char str[55];//模板串,”单词“
char des[1000005];//”文章“
int q[500005], qin, qout;//队列

void cas_init()//在整个程序前构造root
{
    s[0].init();//初始化头结点
    sind = 1;//当前有一个节点
}

void ins()//向书中插入字母
{
    int len = strlen(str);//模板串的长度
    int i, j, ind;
    for(i = ind = 0; i < len; i++)
    {
        j = str[i] - a;//求出字母在next中的编号
        if(s[ind].next[j] == -1)//如为空则构造新的,如不为空则顺着上次的开始往下走构造
        {
            s[sind].init();//初始化当前节点
            s[ind].next[j] = sind++;//连向当前节点,并使sind加一来扩充节点
        }
        ind = s[ind].next[j];//向下遍历
    }
    s[ind].count++;//增加离根节点这条路径上字符串的个数,一条路上可能不止一个单词
}


void make_fail()//构造失配指针
{
    qin = qout = 0;//初始化队列
    int i, ind, ind_f;
    for(i = 0; i < 26; i++)
    {
        if(s[0].next[i] != -1)
        {
            q[qin++] = s[0].next[i];//先考虑根节点,和根节点相连的都入队
        }
    }
    while(qin != qout)
    {
        ind = q[qout++];//记录队首节点
        for(i = 0; i < 26; i++)//遍历队首节点的next
        {
            if(s[ind].next[i] != -1)//如果节点next不为空
            {
                q[qin++] = s[ind].next[i];//将儿子节点入队
                ind_f = s[ind].fail;//记录节点的失配指针指向
                while(ind_f > 0 && s[ind_f].next[i] == -1)//当失配指针不为root时一直循环直到找到一个节点的儿子是i值或到了root
                    ind_f = s[ind_f].fail;
                if(s[ind_f].next[i] != -1)//如果当前节点有儿子的话记录下来备用
                    ind_f = s[ind_f].next[i];
                s[s[ind].next[i]].fail = ind_f;//使当前节点的失配指针指向刚才记录的节点完成失配指针的寻找构造。
            }
        }
    }
}

int fd()
{
    int ct = 0;//记录“单词的个数”
    int di, i, ind, p;//di为指向“文章”的指针,ind为指向失配节点的指针(即trie树中自匹配的指针)与kmp中next数组中的temp很相似
    int len = strlen(des);//“文章的长度”
    for(di = ind = 0; di < len; di++)
    {
        i = des[di] - a;
        while(ind > 0 && s[ind].next[i] == -1)//当ind指针不是root和找不到节点的儿子是i时一直找下去(即kmp中的while循环)
            ind = s[ind].fail;//一直寻找失配指针

        if(s[ind].next[i] != -1)//找到了适合的失配指针
        {
            ind = s[ind].next[i];//指向这个儿子节点,更新ind的值进行下一次匹配

            p = ind;//用p来临时代替ind
            while(p > 0 && s[p].count != -1)//p > 0表示还没到root,count != -1表示指针前还有单词
            {
                ct += s[p].count;//加上有的单词的个数
                s[p].count = -1;//不重复计算,注意这里很重要
                p = s[p].fail;//一直寻找失配指针
            }
        }
    }
    return ct;//返回单词个数
}

int main()
{
    int cas, n;
    scanf("%d", &cas);
    while(cas-- && scanf("%d", &n))
    {
        gets(str);

        cas_init();//初始化trie树
        while(n-- && gets(str))
            ins();//构造trie树

        make_fail();//构造失配指针
        gets(des);
        printf("%d\n", fd());
    }
    return 0;
}

 

AC自动机学习笔记

标签:

原文地址:http://www.cnblogs.com/ws-fqk/p/4649466.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!