转载 - AC自动机算法

出处:http://blog.csdn.net/niushuai666/article/details/7002823

AC自动机简介: 

首先简要介绍一下AC自动机:Aho-Corasick
automation,该算法在1975年产生于贝尔实验室,是著名的多模匹配算法之一。一个常见的例子就是给出n个单词,再给出一段包含m个字符的文章,让你找出有多少个单词在文章里出现过。要搞懂AC自动机,先得有字典树Trie和KMP模式匹配算法的基础知识。KMP算法是单模式串的字符匹配算法,AC自动机是多模式串的字符匹配算法。

AC自动机的构造:

1.构造一棵Trie,作为AC自动机的搜索数据结构。

2.构造fail指针,使当前字符失配时跳转到具有最长公共前后缀的字符继续匹配。如同 KMP算法一样,
AC自动机在匹配时如果当前字符匹配失败,那么利用fail指针进行跳转。由此可知如果跳转,跳转后的串的前缀,必为跳转前的模式串的后缀并且跳转的新位置的深度(匹配字符个数)一定小于跳之前的节点。所以我们可以利用
bfs在 Trie上面进行 fail指针的求解。

3.扫描主串进行匹配。

AC自动机详讲:

我们给出5个单词,say,she,shr,he,her。给定字符串为yasherhs。问多少个单词在字符串中出现过。

一、Trie

首先我们需要建立一棵Trie。但是这棵Trie不是普通的Trie,而是带有一些特殊的性质。

首先会有3个重要的指针,分别为p, p->fail, temp。

1.指针p,指向当前匹配的字符。若p指向root,表示当前匹配的字符序列为空。(root是Trie入口,没有实际含义)。

2.指针p->fail,p的失败指针,指向与字符p相同的结点,若没有,则指向root。

3.指针temp,测试指针(自己命名的,容易理解!~),在建立fail指针时有寻找与p字符匹配的结点的作用,在扫描时作用最大,也最不好理解。

对于Trie树中的一个节点,对应一个序列s[1...m]。此时,p指向字符s[m]。若在下一个字符处失配,即p->next[s[m+1]] ==
NULL,则由失配指针跳到另一个节点(p->fail)处,该节点对应的序列为s[i...m]。若继续失配,则序列依次跳转直到序列为空或出现匹配。在此过程中,p的值一直在变化,但是p对应节点的字符没有发生变化。在此过程中,我们观察可知,最终求得得序列s则为最长公共后缀。另外,由于这个序列是从root开始到某一节点,则说明这个序列有可能是某些序列的前缀。

再次讨论p指针转移的意义。如果p指针在某一字符s[m+1]处失配(即p->next[s[m+1]] ==
NULL),则说明没有单词s[1...m+1]存在。此时,如果p的失配指针指向root,则说明当前序列的任意后缀不会是某个单词的前缀。如果p的失配指针不指向root,则说明序列s[i...m]是某一单词的前缀,于是跳转到p的失配指针,以s[i...m]为前缀继续匹配s[m+1]。

对于已经得到的序列s[1...m],由于s[i...m]可能是某单词的后缀,s[1...j]可能是某单词的前缀,所以s[1...m]中可能会出现单词。此时,p指向已匹配的字符,不能动。于是,令temp
= p,然后依次测试s[1...m], s[i...m]是否是单词。

构造的Trie为:

二、构造失败指针

用BFS来构造失败指针,与KMP算法相似的思想。

首先,root入队,第1次循环时处理与root相连的字符,也就是各个单词的第一个字符h和s,因为第一个字符不匹配需要重新匹配,所以第一个字符都指向root(root是Trie入口,没有实际含义)失败指针的指向对应下图中的(1),(2)两条虚线;第2次进入循环后,从队列中先弹出h,接下来p指向h节点的fail指针指向的节点,也就是root;p=p->fail也就是p=NULL说明匹配序列为空,则把节点e的fail指针指向root表示没有匹配序列,对应图-2中的(3),然后节点e进入队列;第3次循环时,弹出的第一个节点a的操作与上一步操作的节点e相同,把a的fail指针指向root,对应图-2中的(4),并入队;第4次进入循环时,弹出节点h(图中左边那个),这时操作略有不同。由于p->next[i]!=NULL(root有h这个儿子节点,图中右边那个),这样便把左边那个h节点的失败指针指向右边那个root的儿子节点h,对应图-2中的(5),然后h入队。以此类推:在循环结束后,所有的失败指针就是图-2中的这种形式。

三、扫描

构造好Trie和失败指针后,我们就可以对主串进行扫描了。这个过程和KMP算法很类似,但是也有一定的区别,主要是因为AC自动机处理的是多串模式,需要防止遗漏某个单词,所以引入temp指针。

匹配过程分两种情况:(1)当前字符匹配,表示从当前节点沿着树边有一条路径可以到达目标字符,此时只需沿该路径走向下一个节点继续匹配即可,目标字符串指针移向下个字符继续匹配;(2)当前字符不匹配,则去当前节点失败指针所指向的字符继续匹配,匹配过程随着指针指向root结束。重复这2个过程中的任意一个,直到模式串走到结尾为止。

对照上图,看一下模式匹配这个详细的流程,其中模式串为yasherhs。对于i=0,1。Trie中没有对应的路径,故不做任何操作;i=2,3,4时,指针p走到左下节点e。因为节点e的count信息为1,所以cnt+1,并且讲节点e的count值设置为-1,表示改单词已经出现过了,防止重复计数,最后temp指向e节点的失败指针所指向的节点继续查找,以此类推,最后temp指向root,退出while循环,这个过程中count增加了2。表示找到了2个单词she和he。当i=5时,程序进入第5行,p指向其失败指针的节点,也就是右边那个e节点,随后在第6行指向r节点,r节点的count值为1,从而count+1,循环直到temp指向root为止。最后i=6,7时,找不到任何匹配,匹配过程结束。

到此,AC自动机入门知识就讲完了。HDU 2222入门题必须果断A掉,反正我是参考别人代码敲的。。。

AC自动机貌似还有很多需要优化的地方,等把基础搞定之后再学习一下怎么优化吧。。

时间: 2024-12-16 15:06:19

转载 - AC自动机算法的相关文章

AC自动机算法详解

首先简要介绍一下AC自动机:Aho-Corasick automation,该算法在1975年产生于贝尔实验室,是著名的多模匹配算法之一.一个常见的例子就是给出n个单词,再给出一段包含m个字符的文章,让你找出有多少个单词在文章里出现过.要搞懂AC自动机,先得有模式树(字典树)Trie和KMP模式匹配算法的基础知识.AC自动机算法分为3步:构造一棵Trie树,构造失败指针和模式匹配过程.     如果你对KMP算法和了解的话,应该知道KMP算法中的next函数(shift函数或者fail函数)是干

杭电ACM1277——全文检索~~AC自动机算法

题目的意思:给你一篇文章,再给你T个字符串,判断这T个字符串有哪些在文章中出现过. 由于文章很大,普通的方法必定超时,所以需要用 AC自动机算法. AC自动机算法是多模匹配算法之一,主要是用于在一篇文章中,找出给定的N个单词在这篇文章中出现的个数. AC自动机算法,我也是刚刚学习,主要是在建立字典树的基础上,增加了失败指针,提高了匹配的效率.而且最难的是失败指针的建立. 它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高. 对于AC自动机算法,可以参考大神的博客:点击打开链接 里面有

AC自动机算法

AC自动机简介:  首先简要介绍一下AC自动机:Aho-Corasick automation,该算法在1975年产生于贝尔实验室,是著名的多模匹配算法之一.一个常见的例子就是给出n个单词,再给出一段包含m个字符的文章,让你找出有多少个单词在文章里出现过.要搞懂AC自动机,先得有字典树Trie和KMP模式匹配算法的基础知识.KMP算法是单模式串的字符匹配算法,AC自动机是多模式串的字符匹配算法. AC自动机的构造: 1.构造一棵Trie,作为AC自动机的搜索数据结构. 2.构造fail指针,使当

AC自动机算法及模板

关于AC自动机 AC自动机:Aho-Corasickautomation,该算法在1975年产生于贝尔实验室,是著名的多模匹配算法之一.一个常见的例子就是给出n个单词,再给出一段包含m个字符的文章,让你找出有多少个单词在文章里出现过.要搞懂AC自动机,先得有模式树(字典树)Trie和KMP模式匹配算法的基础知识.AC自动机算法分为3步:构造一棵Trie树,构造失败指针和模式匹配过程. 简单来说,AC自动机是用来进行多模式匹配(单个主串,多个模式串)的高效算法. AC自动机的构造过程 使用Aho-

结合双数组Trie的AC自动机算法

结合双数组Trie的AC自动机算法是一种相对比较快的词匹配算法,常见于分词系统用于分词,本文准备用伪代码结合实例的形式来讲解一下该匹配算法的实现原理 构建步骤: 1.Trie树的构建 首先直观感受一下Trie树: 插入模式串 ball.bat.doll.dork.dorm.do.send.sense之后形成的trie树 trie树分三步:插入(构建).查找.删除 插入: 1.输入一个模式串 String 2.如果该模式串已经存在于树中,则跳转 8.如果不存在,跳转3 3.设置当前节点为根节点 4

AC自动机算法学习

KMP+TRIE int val[1000100][31],tot; int tr[1000100]; int fail[1000100]; struct AC_Trie{ void clean(){ tot=0; memset(val,0,sizeof(val)); memset(tr,0,sizeof(tr)); memset(fail,0,sizeof(fail)); } void build(){ queue<int> q; memset(fail,0,sizeof(fail)); w

跳跃表,字典树(单词查找树,Trie树),后缀树,KMP算法,AC 自动机相关算法原理详细汇总

第一部分:跳跃表 本文将总结一种数据结构:跳跃表.前半部分跳跃表性质和操作的介绍直接摘自<让算法的效率跳起来--浅谈"跳跃表"的相关操作及其应用>上海市华东师范大学第二附属中学 魏冉.之后将附上跳跃表的源代码,以及本人对其的了解.难免有错误之处,希望指正,共同进步.谢谢. 跳跃表(Skip List)是1987年才诞生的一种崭新的数据结构,它在进行查找.插入.删除等操作时的期望时间复杂度均为O(logn),有着近乎替代平衡树的本领.而且最重要的一点,就是它的编程复杂度较同类

算法模板——AC自动机

实现功能——输入N,M,提供一个共计N个单词的词典,然后在最后输入的M个字符串中进行多串匹配(关于AC自动机算法,此处不再赘述,详见:Aho-Corasick 多模式匹配算法.AC自动机详解.考虑到有时候字典会相当稀疏,所以引入了chi和bro指针进行优化——其原理比较类似于邻接表,这个东西本身和next数组本质上是一致的,只是chi和bro用于遍历某一节点下的子节点,next用于查询某节点下是否有需要的子节点) 1 type 2 point=^node; 3 node=record 4 ex:

AC自动机——多模式串匹配的算法思想

标准KMP算法用于单一模式串的匹配,即在母串中寻求一个模式串的匹配,但是现在又存在这样的一个问题,如果同时给出多个模式串,要求找到这一系列模式串在母串存在的匹配个数,我们应该如何处理呢? 基于KMP算法,我们能够想到的一个朴素算法就是,枚举这多个模式串,然后进行多次KMP算法,这个过程中完成计数,假设这里有n个模式串,那么整个算法的复杂度大约是O(n*m),m是母串的长度,这里的时间复杂度是粗略估计,没有计算辅助数组的时间(KMP中的next数组),但是这种复杂度还是太高,没有做到KMP算法中“