SAM學習筆記


前(fei)言(hua):

咳咳,既然是學習筆記,那肯定是邊學邊寫的啊,所以會持續更新呀。

 

SAM是啥子玩意?(在大佬講之前完全聽都沒聽過,但是根據PPT的排布以及講的是字符串主題來看,這應該是“后綴自動機”了(本蒟蒻聽過后綴自動機,僅僅只是聽過,但是SAM這個名字就沒聽過了))

 

然后我百度了一下SAM,搜出來這玩意......

 

噫!好!我人傻了!當場爆粗口來了句氧化鈣。

 

然后我耐心(氣急敗壞)的查了查“后綴自動機”|,終於正常了。

 

正題:

后綴自動機 (suffix automaton, SAM) 是一個能解決許多字符串相關問題的有力的數據結構(”高級算法“)。

 

后綴自動機中幾個重要的概念:

1.endpos(S)表示的是子串S在原串中出現的位置(末位置),例如在ababa中,endpos(ab)={2,4}(好像也有稱為right集合的,不管了),這玩意是個等價集

 

2.如果存在子串T以及S使得endpos(T)=endpos(S),那么我們則將S以及T歸為同一類中,endpos相等的子串歸在一起稱為一個"狀態",

我們給各個"狀態"依次編號,那么就會得到若干個狀態,在這里我們約定我們記為Str(x)表示狀態x的所有字符串(x是狀態的編號)

同樣拿上面的例子來說事,endpos(ab)={2,4},endpos(b)={2,4},這樣我們將"ab"以及"b"歸為一類,稱為一個"狀態"

 

3.后綴鏈接,這玩意下文詳細講

 

4.parent樹,這玩意下文詳細講

 

后綴自動機的幾個性質

先看一組endpos的數據:

當原串為:“abbabaa”的時候

狀態1.   空串                                      endpos={1,2,3,4,5,6,7}

狀態2.  a                                             endpos={1,4,6,7}

狀態3.  ab                                          endpos={2,5}

狀態4. b                               endpos={2,3,5}

狀態5.abb,bb                        endpos={3}

狀態6.abba,bba                  endpos={4}

狀態7.ba                                                                                 endpos={4,6}

狀態8.abbab,bbab,bab                 endpos={5}

狀態9.abbaba,bbaba,baba,aba           endpos={6}

狀態10.abbabaa,bbabaa,babaa,abaa,baa,aa          endpos={7}

話說,泥萌有沒有發現什么規律。

對於每一個狀態,里面每一個串的長度都是逐一遞減的,而且后一個串就是前一個串去掉第一個字符后得到的后綴

 

那么性質一來了:

如果endpos(S)=endpos(T),那么S為T的后綴或者T為S的后綴

不妨令|S|<|T|,那么S一定是T的后綴

證明:如果S不是T的后綴,那么S的第1到len(S)的字符中必然有一位與對應的T的第len(T)-len(S)+1到len(T)位不同

因為endpos(S)=endpos(T),顯然與以上假設矛盾,那么不可能有S的第1到len(S)的字符中必然有一位與對應的T的第len(T)-len(S)+1到len(T)位不同

所以如果endpos(S)=endpos(T),那么S為T的后綴或者T為S的后綴.

 

性質二:

觀察得到,如果S為T的后綴,則endpos(S)包含集合endpos(T)

如果S不是T的后綴,則endpos(S)∩endpos(T)=∅

 

性質三(轉移函數)**:

挺重要的。

對於一個狀態X,我們在屬於它的串后面加上一個字符ch,只要這個字符的是屬於由      endpos(狀態X中的字符串)后一個的字符組成的集合nxt,

那么就滿足產生的新串都屬於同一個狀態(這里可能我表述不大清楚,多給幾個例子,方便理解)

例如上面的例子:

對於開篇的串的狀態6,endpos()={4},那么nxt集合等於{"b"}

那么定然abba+b,bba+b屬於一個同一個狀態

對於串“abdabe”中

某個狀態為: ab,b             endpos={2,5}

那么ab+d與b+d一定是屬於同一類,

ab+e 與 b+e一定是同一類。

對於串"acbabacba"中

某個狀態為:acba,cba       endpos={4,9}

那么對acba+b 與 cba+b一定是同一類的

 那么我們將會得到一個函數:f(S,c)=str(S)+(c∈nxt),S為狀態號,nxt可以通過endpos求得,也就是s[endpos(str(S))+1]所得到的字符集

這個函數將會用來進行匹配以及構建SAM!!!!

性質四:

 前面說了“對於每一個狀態,里面每一個串的長度都是逐一遞減的,而且后一個串就是前一個串去掉第一個字符后得到的后綴”,那么我們觀察得到每個狀態都是“不完全的”

所謂的不完全就是無法直接從狀態中的最長串一直延續到空串

例如“狀態9.abbaba,bbaba,baba,aba           endpos={6}”

如果它是完全的,那么就會有接下來的:

“狀態9.abbaba,bbaba,baba,aba,ba,a           endpos={6}”,

顯然a與ba的endpos不與前面幾項endpos相同對不對?所以它們不是同一個狀態

我們要是強行將狀態9完善呢?那么就會需要將“ba”以及“a”接進來,那么就需要一條鏈,這條鏈為:

狀態9----->狀態7------>狀態2----->狀態1

這條鏈肯定是唯一性的(不會有一模一樣的兩條鏈),並且被指向的那個狀態所包含的字符串一定是當前狀態     所有*(一定是的,因為串中字符串的長度是嚴格遞減的)     字符串的后綴。

同時這些鏈它們的終點一定會空集,也就是狀態1,所以,把狀態1看成根節點的話,若干條這樣的鏈也就構成了一棵樹,也就是大名鼎鼎的parent樹,我們驚奇的發現我們要的后綴自動機的節點就是parent樹的節點!

 

bb了這么多(我太菜了,因為是邊理解邊寫的,可能會有些誤差,希望大佬們能夠指出),現在終於到了講構造后綴自動機的時候了(激動的小手准備開始打板子了)!

 

還是先講一下構造的思路(只貼代碼不講思路的博客都是耍流氓):

 后綴自動機的構造是在線的,我們通過每次插入節點的方式來實現(有之前手寫二叉堆內味了)

    • last 為添加字符 c 之前,整個字符串對應的狀態(一開始我們設 last=0 ,算法的最后一步更新 last )。
    • 創建一個新的狀態 cur,並將 len(cur) 賦值為 len(last)+1 ,在這時 link(cur) 的值還未知。
    • 現在我們按以下流程進行(從狀態 last 開始)。如果還沒有到字符 c的轉移,我們就添加一個到狀態 cur 的轉移,遍歷后綴鏈接。如果在某個點已經存在到字符 c 的轉移,我們就停下來,並將這個狀態標記為p 。
    • 如果沒有找到這樣的狀態 p ,我們就到達了虛擬狀態 1 ,我們將 link(cur) 賦值為 0並退出。
    • 假設現在我們找到了一個狀態 p ,其可以通過字符 c 轉移。我們將轉移到的狀態標記為 qqq 。
    • 現在我們分類討論兩種狀態,要么 len(p)+1=len(q) ,要么不是。
    • 如果 len(p)+1=len(q),我們只要將 link(cur) 賦值為 q 並退出。
    • 否則就會有些復雜。需要 復制 狀態 q :我們創建一個新的狀態 clone ,復制 q的除了 len 的值以外的所有信息(后綴鏈接和轉移)。我們將len(clone) 賦值為 len(p)+1 。
      復制之后,我們將后綴鏈接從 cur 指向clone ,也從 q 指向 clone 。
      最終我們需要使用后綴鏈接從狀態 p 往回走,只要存在一條通過 ppp 到狀態 q 的轉移,就將該轉移重定向到狀態 clone 。
    • 以上三種情況,在完成這個過程之后,我們將 last 的值更新為狀態cur 。

以上帶點內容借鑒(抄襲)於:[ HatsuneMiku神佬的題解](https://www.luogu.com.cn/problem/solution/P3804)

代碼就先咕咕了。。。。。

鳴謝:sh妹,Rothen,濤隊,萬總幫助小蒟蒻MYCui


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM