後綴自動機總結

學了一發後綴自動機……然而特麼做題全是看的題解(畢竟pb太弱)……
這裏就不寫後綴自動機的構造和原理了吧……網上多的是……(事實上是pb太弱不懂……)
1.codevs 3160最長公共子串
題目都一句話了……我就懶得說了……
這是後綴自動機必做的裸題吧……
先把第一個字符串建一個後綴自動機,然後拿第二個串在後綴自動機上跑,優先跑能走到的字符,如果走不到就走pre節點,再看能不能走這個字符,如果pre節點跑完了都沒法走的話,就把現在長度賦爲0繼續跑下一個字符,每次現在長度變大時都更新一下答案。
2.codevs 1372DNA
cv上這題的數據很水……亂搞都能A,但如果想弄懂SAM的話,請認真對待每一題。
這題實際上也就是一個求最長公共子串……把給你的那個字符串取反後倒過來再跑原串建出來的後綴自動機就好了。
3.codevs 1500後綴排序
題意:給你一個串,然後對它的所有後綴從小到大排序,按排序後的順序輸出每個後綴第一個字母在原串的位置。
我們可以先建出後綴樹,也就是SAM的pre樹,然後我們標記一下每個節點是否是原串上的點(換句話 說,標記這個點是否是個複製後的點),然後直接按字典序跑一遍就好了。
4.UOJ#35後綴排序
這題比上一題多了一個要求排序後任意兩相鄰的後綴的LCP,那麼這個實際上就是SA的height數組,所以更像是SA的模板題,但是SAM也是可以做的,我們在後綴樹上跑的時候,記錄一下LCA的所表示的節點的step,由於後綴樹上的葉子節點必定是一個後綴,所以我們在dfs到某個點的時候,如果它是一個原串上的點,我們直接更新step的值,之後先dfs,再把step改爲當前節點的step。
5.BZOJ 3238差異
這題跟上題也有點像……只不過我們需要記錄一個子樹的size……以及子樹中每個後綴的長度和,然後dfs到x這個點的時候,對於每個son,就用乘法原理就好了。
6.BZOJ 4199品酒大會
有了上一題的基礎那麼這題也比較容易了,上題是記錄子樹和,這題記錄子樹最大值就好了,由於有負數,所以還要記錄一個子樹最小值。爲什麼不要記錄次大和次小?因爲你在每個節點都更新一遍答案的話,所有情況都可以只用當前子樹的最大值和最小表示出來,至於爲什麼可以考慮一下後綴樹的結構。
7.SPOJ LCS2
題目大意是說有n個由小寫字母組成的字符串,求他們的最長公共子串。
我們考慮做只有兩個字符串的LCS時候的做法,那麼n個字符串的話,我們只要對於SAM上的每個狀態都記錄一下第i個字符串到當前狀態的最長公共子串的長度是多長即可,然後對於每個狀態上的n個值取min,最後再對每個狀態取max就是答案,注意由於當前節點能接受的後綴pre必定能接受,所以我們需要在pre樹上求子樹最大值,這纔是第i個串在當前狀態的答案,注意細節。
8.BZOJ 4566找相同字符
這題既可以用單串SAM也可以用多串SAM,先說多串SAM的做法。
不難發現對於SAM上的一個狀態,如果兩個串都能匹配到當前節點,那麼這兩個串在這個節點就是相同的,於是我們對於兩個串分開記錄他們的right集合大小,right相乘再乘上當前節點代表的字符串個數之和就是答案了,當前節點代表的字符串個數之和是當前節點的step-它的pre的step(原因在麗潔姐姐的PPT上已經講的很詳細了)。
接下來講單串SAM的做法。
我們先對第一個串建SAM,考慮第二個串怎麼辦。首先,我們可以直接把第二個串放在後綴自動機上運行,類似於求LCS那樣,這樣的話,我們對於當前狀態,如果它包含了整個節點(也就是匹配長度=當前節點的len),我們稱該節點爲完整節點,否則爲不完整節點。那麼對於不完整節點的狀態,我們可以直接計算它的字符串個數,而對於完整節點的狀態,我們需要先打上標記,表示這個地方被完美匹配了一次,最後在pre樹上dfs,對於一個節點,它的貢獻是子樹中的標記數量它的right集合大小它所代表的不同字符串個數。這個是爲什麼呢?pre還有一個性質,那就是pre代表的字符串必定是當前節點的後綴,因此當前節點的出現次數pre顯然也是出現了這麼多次的。
9.ICPC camp2017某題
題目大意:給定一個由小寫字符組成的字符串,求它有多少個子串滿足循環左移一位後還是原字符串上的子串,n<=3e5。
這題出的非常妙啊……考察了很多對SAM的理解……
顯然我們需要枚舉一個東西,在不TLE的情況下,那自然枚舉後綴自動機的節點是墜吼的。
我們來考慮SAM的性質,首先,如果當前節點的next數組有某個字符,那麼說明這個字符接在當前節點組成的字符串的後面也是一個原串的子串。
其次,對於一個節點,它所能代表的不同的字符串個數,假如不是最長的它所能代表的字符串,那麼它的前一個字符是固定的,這爲我們循環左移提供了方便,對於這個固定的字符,我們可以直接看它的next數組中是否有這個字符。
這樣,我們得到了一個初步的算法,枚舉SAM的節點,設當前枚舉的節點代表的字符串爲s,對於每個不是當前節點表示的最長串的字符串,直接判斷SAM的next數組中是否有這個字符,但是我們發現一個問題,那就是直接枚舉節點所能代表的字符串複雜度是n^2的,那不妨換一種角度,我們枚舉當前節點的後一位,那麼當前節點所代表的字符串實際上是一個區間,那麼用前綴和可以O(1)求出每個區間的某個字符的出現次數,這樣,我們相當於是枚舉sc,(c爲一個字符),求是否有cs在原串中出現過,注意求區間的時候判斷一下pos

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章