后綴自動機經典操作


看了幾天的后綴自動機,感覺這玩意兒確實比較神奇。但是感覺自己肯定講不明白,就簡單的來寫寫心得和應用吧

性質

1、每個狀態$s$代表的長度區間為$(len[fa[s]],len[s])$

也就是說$min(s) = max(s) + 1$

 

2、每個狀態$s$代表的所有串在原串中的出現次數及出現位置右端點相同。

這也是后綴自動機能夠壓縮狀態的原因,就是把很多相同的串壓縮到一個節點中

 

3、在parent樹中,對於狀態$s$,$fa[s]$所代表的狀態是$s$所代表狀態的后綴

 

4、在parent樹中,每個狀態的$right$集合是它父節點$right$集合的子集

由性質$3$很容易得到$fa[s]$所代表的狀態是$s$所代表狀態的后綴,那么$fa[x]$所代表的子串的出現位置一定比$s$代表子串的出現位置多

 

5、對轉移邊形成的DAG拓撲排序后,每個節點對應的大小為:以該節點為起點的子串的數量(本質相同的子串算一個)

 

6、對$fa$邊形成的樹拓撲排序后,每個節點對應的大小為該節點對應$right$集合的大小

$fa[s]$表示的是$s$的前綴,那么$s$出現的地方$fa[s]$也一定出現

 

經典應用

求兩個串的最長公共子串

首先把第一個串的SAM建出來,

枚舉第二個串,同時沿着轉移邊進行匹配,若匹配失敗,那么就沿着$fa$邊向上走,

匹配的同時記錄一下$max$

SPOJ1811 LCS 

求多個串的最長公共子串

網上的做法基本都是對第一個串建SAM,然后枚舉其他的串,在這個串上匹配。

但是貌似要考慮很多東西??

這里我將一個比較naive但是不會TLE的做法。

最終的答案一定出現在第一個串中,所以可以把第$2-N$個串的SAM建出來

然后枚舉第一個串的每一位,在其余的SAM中匹配。

雖然聽着嚇人,但是代碼十分好寫

BZOJ2946 [Poi2000]公共串

求第$k$小子串

我們可以通過對轉移邊$dfs$而求出以該節點為起點的子串的大小

開始時從$root$開始走,每次優先選擇字典序小的轉移邊,

若該出邊對應的大小$<k$,說明答案不在該出邊所對應的字符串中,令$k$減去該節點的大小,繼續匹配

若該出邊對於的大小$>=k$,說明答案在該出邊中,那么沿着該出邊繼續走

 

注意在求第$k$小子串的時候要考慮本質相同的子串是否重復統計的問題

如果要重復統計,則加上$right$集合的大小就可以了

 

不重復統計SPOJ7258 SUBLEX

重復統計BZOJ3998: [TJOI2015]弦論

 

字符串最小表示法

最小表示法的定義:

字符串$S$的最小表示法為,對於任意的$i \in [1, |S|]$,把$[1,i]$對應的字符串剪切到$S$尾所形成的字符串中,字典序最小的一個

 

字符串的最小表示有它自己的算法,可以參考這里

當然后綴自動機也是可以搞的,我們首先把字符串復制一遍,扔到SAM里,

然后從根節點出發貪心的走較小的出邊,同時輸出每一次經過的字符,當達到$N$次時停止。

 

但是我還是建議大家學一下最小表示法的標准算法, 因為后綴自動機需要$4*|S|*siz$的空間($siz$表示字符集),很容易被卡掉

POJ1509 Glass Beads

 

求本質不同的串的數量

考慮到每個狀態表示的子串是兩兩不同的,

根據性質1每個狀態$s$代表的長度區間為$(len[fa[s]],len[s]]$

然后對所有節點求個和就好,答案為$\sum_{t} len[t] - len[fa[t]]$

 


免責聲明!

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



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