後綴自動機概念的溫習

以爲對於一個數據結構充分的學會使用,必定要對它的構成部分和定義概念有很充分很全面的瞭解.算法

因此我以爲的一種溫習的最好方式就是:明晰概念->分析運用這樣的層次.數據結構

概念的明晰確實是十分有用的,它既是理解他人算法的必要前提,也是你在看到題目後能夠有創新想法的重要基礎.spa

 

下面這篇文章主要是寫給筆者本身看的...因此邏輯有點混亂是由於寫的順序不是這樣一路下去的...233指針

 

1.狀態集合blog

  每一個狀態中存儲的是一些right集合相同的字符串.初始態的right集合視爲{1,2,3...n} [可是例如"aaa"中,"a"的right雖然也是{1,2,3}可是不與初始態重合,這個例子的parent樹中初始態只有一個葉子節點].排序

  right相同的字符串的話,就不要以爲它們之間是沒有聯繫的,它們應該是連續的是相互重疊的.字符串

  好比說"abcdefdef中": "abcdef","bcdef","cdef" 就是放在一個right集合中的.
入門

  狀態中出現了一個屬性mx,表示的是這個right集合中線段的最長長度.基礎

  回顧構造sam的過程,mx的賦值是在實邊的基礎上賦值的.好比當末尾元素加入的時候mx[np]=mx[p]+1,由於當實邊鏈接的時候表示前一個集合的某些位置能夠日後向x的方向拓展一步,那麼全部能拓展的子串中選最長的+1就是新的right集合中最長的子串了,而因爲咱們是沿着parent往上走,因此parent的前進至關於刪去一個首字母獲得原先串的一個後綴,因此最底下的串是最長的,因此也能夠想象獲得新增長的這個節點中的子串必定知足上面說的性質連續的.
變量

  若是兩個狀態u,v,v--x-->u 且 mx[u]=mx[v]+1,則說明u中最長的串能夠直接由v的最長串得來.v是全部能經過x到u的串中最長的,其它的串是它的後綴,且不被其餘任何串包含.

 

2.狀態之間的聯繫

  1.實邊:

    若是在一個串的後面加上字符,那麼right集合必定發生了改變.

    實邊鏈接表示一個right集合的出現,這個right集合應該是前一個right集合中的部分位置日後+1獲得的新right集合

    例如"abcdeabcdf"中R("abcd")={4,6}--e-->R("abcde")={5}

                       --f-->R("abcdf")={7}

    因此說若是有實邊相連說明在串中的某個位置能夠接着日後走這個元素到達新的一個狀態.

    圖像感覺就是如今線段上你有不少個小點[表示當前狀態的right集合]而後你能夠在某些位置後面找到一個元素走一步,變成新的一些小點.

  2.parent邊:

    每一個狀態中存下的是一段連續的串,其中最短的那個串去掉首字母就不在這個狀態中了.那麼就是到了parent節點中.parent(x)就是right集合包含x的全部中最小的集合.

    因此parent鏈往下至關於trans反過來的一個過程:首先你有一個線段上的小點,而後某些位置上你能夠往前找到某一個字符,而後知足這個條件的某些小點就構成了parent掛下的right集合.

    好比下圖中的邊就表示的往前加入的一個字符。固然咱們能夠發現,加了字符以後能出現新right集合的條件就是你得有幾種加法,好比"cd"能夠加"b"或者"d";"bcd"能夠加"a"或者"d"。

    這個的正確性比較顯然...若是每一個地方都只能加同樣的...固然新串的right集合和本身這個相同咯...

    

    

    而反過來沿着parent鏈往上又是集合合併的過程。唔,這個的正確性你能夠感覺一下上面那個逆過程,想必是能理解的。

    固然咱們在這裏就發現了trans和parent的區別。

    1.trans是在原來串的基礎上日後加字符,parent是往前加。因此一個是right集合中選一部分出來+1做爲新的right,一個是直接從right集合中選一部分出來。

    2.trans是隻要後面能加值就必定能產生新的狀態,parent是必需要有至少兩條不一樣的才能產生新的right集合。

 

3.狀態之間的路徑:

  1.實邊構成的路徑

    從初始態沿着實邊到達某個狀態的若干條路徑即這個狀態中全部的串.若某個狀態指的是終止態,那麼這是原串的一條後綴.

    任意兩個狀態之間的路徑表示的是一個匹配的過程.仍是回到圖形上去.一開始你有一條線段上的若干個點,而後你沿着你須要匹配的鏈,從這些點中選出你須要的點並將位置+1,而後再下一步再選出一些點,再+1...以此下去直到最後你到達的那個狀態.其中就是不斷選擇知足條件的right向後延伸的過程.也至關於一個匹配的過程.

  2.parent構成的鏈

    parent構成的鏈是向上的不斷的去掉首字母的過程,其祖先都是它的一個後綴.parent樹上不是向上的邊構成的路徑意義不是很大.可是兩個點的lca表示它們的最長公共後綴.

 

大概反思了一下全部的概念,而後就能夠分析一些SAM處理的過程:

 

1.構造:

  構造是在串"S"所構成的SAM的基礎上日後拓展一位c獲得的SAM.因爲SAM須要識別全部的子串,其中不包括c的子串已經獲得,須要識別全部包括c的子串,也就是後綴.

  首先須要構造一個新的終止態np.由於終止態的最長串天然是整個串,因此mx[np]=mx[p]+1,而後由於是識別後綴,就應該是在原來的後綴基礎上向後拓展c.找到全部後綴的方法很簡單,首先找到了包括全部後綴的"S",而後沿着parent鏈往上就是一個不斷取後綴的過程.

  對於一些本來沒有連出c邊的後綴,也就是它們在線段上的小點中沒有一個位置能向後拓展一步c,如今它們在末尾能夠拓展了,因此a[x][c]=np.

  對於某些後面本來連出c邊的後綴"A",它們在線段上的小點中有某些位置日後走一步c,這樣的話還要看是否是有包含了這個後綴而又不是原串後綴的串"B",若是有,那麼這個後綴日後走一步c到達的狀態的最長串就不是"Ac"而是"Bc",那麼"Ac"的right集合會變化,可是"Bc"到"Ac"的這一段的right集合卻不會變化.因此須要將這個狀態分紅兩個部分,新的部分的right集合須要加入新的一個點.即fa[np]=nq,這個新的狀態的最長長度就是"A"長度+1,即mx[nq]=mx[p]+1;同時也要包含之前的right集合,即fa[q]=nq.再看一下接着往上去的話,由於構成的是一棵parent樹,"A"所在的狀態至關於一個分叉點,再往上就不會分叉了.因此上面的right都會增長新的那個節點,即fa[nq]=fa[q].既然產生了新的,可能還會有別的後綴也擴展到"A"所擴展的狀態去,因此須要把全部fa[x][c]==q的所有改爲nq.固然若是知足了mx[q]==mx[p]+1就沒有這麼麻煩了,只須要給這個right集合擴充就行.fa[np]=q.而後整個過程就完成了.

因此若是說讀者您看過我以前有一篇入門的後綴自動機的話,能夠發現那張解釋構造的圖其實有一點小問題.但願你理解以後能夠本身發現.

 

2.尋找最長公共子串:

  首先給第一個串創建sam,而後讓別的串在這個自動機上走.

  思考這個走的過程.如今咱們在x狀態,而後能夠日後看是否具備一個當前字符的轉移,若是有,就至關於把全部當前狀態中能夠轉移的小點都往前走了一步.若是沒有,那麼就沒法找到這麼多匹配的,可是要利用已經匹配的信息,因此咱們能夠退回到當前串的後綴去看,而後把已經匹配的長度變成mx[S],至關於咱們捨棄了這個狀態到上一個狀態的前面的部分,而後再去嘗試往x的方向走.固然若是找了全部的,甚至到了初始態都沒有x出邊,那麼就說明沒有匹配,已匹配長度設置爲0,指針指向初始態.

  當咱們處於某個狀態的時候,其實並不能判斷咱們匹配的是這個狀態中的哪個串,可是咱們卻知道咱們匹配到了原串的哪幾個位置.也就是知道下一個能夠往哪一個方向走.若是要知道匹配了多長則須要記錄一個變量,可是若是是發現此處不能向後拓展後的沿着parent往回跳,那麼就必定能夠匹配到parent中的最長的那個串.

  尋找多個串的公共子串的時候,能夠記錄一下每一個節點在每一個位置匹配到的長度,而後對於全部的串在該位置上取一個最小值就是全部串公共的部分了.可是有要注意的地方就是若是你匹配到了某一個狀態,那麼它的parent中的全部的值其實你也能夠匹配到,因此須要拓撲排序處理一下,把每一個匹配到的值往上回溯更新,防止出現疏漏.

相關文章
相關標籤/搜索