AC自动机学习笔记

搞了一晚上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;
}
时间: 2024-08-07 09:03:38

AC自动机学习笔记的相关文章

AC自动机 - 学习笔记

AC自动机 ----多个模板的字符串匹配 字典树Trie加上失配边构成 struct ACauto { int ch[MAXN][26]; int size; int f[MAXN],last[MAXN],val[MAXN],cnt[MAXN]; //val用来在字典树中的模板串末尾处标记,标记为模板串的序号(从1开始) //last后缀链接:结点J沿着失配指针往回走时,遇到的下一个单词尾结点. void init()//初始化 { size=1;//字典树中的节点数 memset(ch[0],

hdu2222 Keywords Search &amp; AC自动机学习小结

传送门:http://http://acm.hdu.edu.cn/showproblem.php?pid=2222 思路:AC自动机入门题,直接上AC自动机即可. 对于构建AC自动机,我们要做的只有三件事: 1)构建字典树 2)构建失败指针 3)构建trie图(这道题好像不做这一步也能A...但是这一步不做是会被卡成O(n^2)的...) 1)第一步还是比较好理解的 根是虚根,边代表字母,那么根到终止节点的路径就是一个字符串,这样对于前缀相同的字符串我们就可以省下存公共前缀的空间. 加入一个模式

AC自动机 学习链接

这个地方写得不错:http://www.cppblog.com/mythit/archive/2009/04/21/80633.html 其实AC自动机是借用了KMP的next(fail)来进行实现 具体是在一个Trie(字典树)中来实现的 时间复杂度O(n+k) (k为待处理子串长度) 所以KMP真是一个神奇的算法,特别是next数组的构造.

AC自动机学习小结

AC自动机 简要说明 \(AC\) 自动机,全称 \(Aho-Corasick\ automaton\) ,是一种有限状态自动机,应用于多模式串匹配.在 \(OI\) 中通常搭配 \(dp\) 食用.因为它是状态自动机. 感性理解:在 \(Trie\) 树上加上 \(fail\) 指针.具体的讲解可以去看dalao们的博客(因为我实在是太菜了讲不好). 题目 Keywords Search 题目:给若干个模式串,再给一个文本串,问有几个模式串在文本串中出现过. 板子题.注意一个模式串只被计算一次

AC算法学习笔记

1.算法流程图 (1)    void Init() 此函数是初始化函数,用来给fail数组和goto数组初始化值. (2)    void GotoFunction(string x) 这个函数的作用是生成有限自动机状态转移图. (3) void FailFunction(int target,int k) 这是fail函数,核心内容是求出每个状态的fail值. (4) void UpdateOutput() 这是update输出函数.其作用是更新每个状态的输出值. (5)void Check

AC自动机--summer-work之我连模板题都做不出

这章对现在的我来说有点难,要是不写点东西,三天后怕是就一无所有了. 但写这个没有营养的blog的目的真的不是做题或提升,只是学习学习代码和理解一些概念. 现在对AC自动机的理解还十分浅薄,这里先贴上目前我看过的文章: 深入理解Aho-Corasick自动机算法 AC 自动机学习笔记 AC自动机相比Trie多了失配边,结点到结点间的状态转移,结点到根的状态转移. 这里fail的定义是:使当前字符失配时跳转到另一段从root开始每一个字符都与当前已匹配字符段某一个后缀完全相同且长度最大的位置继续匹配

学习笔记(SAM)

后缀自动机学习笔记 性质 首先,你最好认为每条边是一个字母,每个节点代表一个单词(从t0走到这个点即是此单词的一个后缀). 所有的正向边,从t0出发沿着nex形成的所有正向边构成一个DEG图,而所有的反向边,即link构成一颗t0为根的树,显然,此树上所有点到根形成的后缀都会被其儿子到根形成的后缀所包含 算法实现 首先写个自定义函数,一般都叫sa_extend() 我们默认根t0是0,他的link(父亲)为-1. 首先新申请一个节点cur,我每次将上一个节点叫做last,先将len[cur]=l

浩爷AC自动机快速学习方案

    今天弄完自动机之后,从那天比赛的阴影中爬出来了,猛地一看真不咋滴难,仔细一看这尼玛还不如猛的一看...     必备算法:KMP,字典树(KMP我写了,字典树太简单,就是一个思想,我可以一个图教你做人)     先讲一下字典树:看图     好了,字典树就看酱紫一个图,你要是脑残就装不懂吧!!    下面是AC自动机的正题:     正如KMP中的求next函数是同样的一个原理     节点的属性:二十六个字母的指针,fail指针,其他...     重点:fail指针,我把它叫做同级指

AC自动机笔记

AC自动机 1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cmath> 5 #include<queue> 6 #define MAX 100001 7 #define u(n) (n-'a') 8 using namespace std; 9 struct ac{ 10 int son[26],fail,sum; 11 void init(){ 12