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指針都指向本身的最長後綴,那麼很重要的一個性質就是讓一個結點cur的fail指針不斷回溯向上走,直到碰到根結點爲止,那麼回溯時通過的結點所表明的字符串都是結點cur所表明的字符串的後綴。htm
下面的第一幅圖是AC自動機,第二幅圖是Fail樹。之因此第一幅圖是AC自動機而不是Trie圖的緣由是Trie圖太特麼難畫了。不過具體的原理仍是沒有變的。排序
能夠看出Fail樹其實就是將AC自動機的next指針去掉,而後反轉fail指針的指向所構造出來了,並且能夠確定這必定是一棵樹 ,因此稱之爲Fail樹。字符串
Fail樹的一個性質是,某個結點所對應的字符串確定是其兒子結點,孫子結點. . .所對應的字符串的後綴。get
若是有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