Trie图和Fail树

Trie图和AC自动机的区别

Trie图是AC自动机的肯定化形式,即把每一个结点不存在字符的next指针都补全了。这样作的好处是使得构造fail指针时不须要next指针为空而须要不断回溯。html

好比构造next[cur][i]的fail指针,cur为父节点,next[cur][i]为cur的儿子结点,若是是AC自动机,若是父亲结点tmp(tmp是cur的一份拷贝)的next[fail[tmp]][i]不存在时,须要让tmp不断回溯(即tmp = fail[tmp]),直到next[fail[tmp]][i]不为空时,才让fail[next[cur][i]] = next[fail[tmp]][i]。数组

若是是Trie图,那么直接让fail[next[cur][i]] = next[fail[cur]][i]就能够了,由于Trie图已经补全了next指针。post

可是无论是Trie图仍是AC自动机,它们的fail指针的指向都是如出一辙的。因此无论是用Trie图仍是AC自动机均可以构造fail树。不过Trie图比AC自动机好写多了,因此我一直都是写Trie图而不是自动机。spa

 

fail指针的性质

要可以灵活使用Fail树,首先须要了解fail指针的性质,因此先说下fail指针都有哪些性质。指针

每一个结点的fail指针都指向本身的最长后缀,那么很重要的一个性质就是让一个结点cur的fail指针不断回溯向上走,直到碰到根结点为止,那么回溯时通过的结点所表明的字符串都是结点cur所表明的字符串的后缀。htm

 

什么是Fail树

下面的第一幅图是AC自动机,第二幅图是Fail树。之因此第一幅图是AC自动机而不是Trie图的缘由是Trie图太特么难画了。不过具体的原理仍是没有变的。排序

能够看出Fail树其实就是将AC自动机的next指针去掉,而后反转fail指针的指向所构造出来了,并且能够确定这必定是一棵树 ,因此称之为Fail树。字符串

Fail树的一个性质是,某个结点所对应的字符串确定是其儿子结点,孙子结点. . .所对应的字符串的后缀。get

 

Fail树的应用

若是有n个字符串,全部字符串的长度加起来不超过$10^6$,有m个查询,要查询第x个字符串在第y个字符串中出现了多少次。博客

若是是使用AC自动机查询,能够直接对字符串构建AC自动机,而后让y去走AC自动机,对于走过的结点,把其权值加1。那么要查询x在y中出现了多少次,便要从底层开始,顺着fail指针把权值上传。而后只要查询x结点的权值是多少就知道x在y中出现了多少次。每次查询的复杂度是O(tot+len[y]),其中tot是AC自动机的结点总数。

若是是使用Fail树进行查询,那么只要查询全部子结点的权值和就行了,子结点的权值和能够使用dfs序和树状数组来维护。而后一样让有去走AC自动机,将走过的结点的权值加1,只不过如今是用树状数组来维护权值。那么要查询x在y中出现了多少次,只要进行一次区间查询就能够了,即只要查询x结点的全部子结点就行了(根据fail树的性质),由于其dfs序号是连续的,因此是一次区间查询。能够将查询按照y排序,而后对具备相同y的查询一块儿查询。每次查询时间复杂度是O(len[y]+log(tot))。

 

该文章在个人我的博客地址是:http://www.alphaway.org/post-440.html

相关文章
相关标签/搜索