字尾自動機學習筆記
字尾自動機大型總結
終於填上這個坑了woc
怎麼實現的以及原理不想寫了
總結一下套路和性質?
基本問題
1、 求多個串的\(\text{LCS}\) 。
有兩個做法,只會一種,就是對第一個串建\(\text{SAM}\) ,剩下的串在\(\text{SAM}\) 上跑,在每個節點記錄一下當前串的最長匹配長度,以及所有串的最短匹配長度。最長匹配長度要對子樹取\(\max\) 因為能匹配一個點的孩子肯定能匹配該點。
2、 統計某個子串的出現次數。
這個可以放在\(\text{SAM}\) 上跑匹配,假設匹配到了點\(x\) ,那就在\(\text{parent}\) 樹上一直跳\(\text{father}\) 並且還要滿足當前點的\(\text{len}\) 要大於等於該子串的長度。最後跳到的點在\(\text{parent}\) 樹上的\(\text{size}\) 就是答案。
3、 求一個字串的最小表示。
可以把串倍長一遍接在後面,然後對新串建\(\text{SAM}\) ,在上面每個點都選一個字典序最小的出邊跑過去就好了,這樣肯定能找到長度為原長的某個子串。
4、 求排名第\(k\) 小的子串(本質相同算一種/多種)。
首先記錄\(f[i]\) 表示從自動機上點\(i\) 開始,有多少個不同的子串。\(f[i]\) 的初值是\(size[i]\) ,如果本質相同算一種,那麼\(size[i]=1\) ,否則\(size[i]\) 為\(\text{parent}\) 樹上點\(i\) 的子樹\(\text{size}\) 。為什麼要這麼設初值?這兩種問題的區別就是,可以在每個點“結束”多少次,或者說從自動機上的點開始走,那可以在原串的幾個位置走。然後在\(\text{DAG}\) 上\(\text{DP}\) 出來所有點的\(f\) 值。接著從小到大類似平衡樹找第\(k\) 大那樣找就好了。注意每次走過一條邊都可以直接在當前點結束一個子串
,所以每走過一個點都需要減去當前點的
\(size\)
,表示可以在這裡就產生一個子串。
5、 給定一個主串和一個詢問串,多組詢問,每次求詢問串的\([l,r]\) 能否在主串中匹配。
可以先把整個詢問串拿上主串的\(\text{SAM}\) 跑一遍匹配,在第\(i\) 位記錄\(mx[i]\) 表示詢問串匹配的右端點是\(i\) 時,最長能匹配多長。根據單調性,如果可以匹配\([i-mx[i]+1,i]\) ,那一定可以匹配\([i-mx[i]+2,i]\) 。
6、 給定一個主串和一個詢問串,多組詢問,每次求詢問串的\([l,r]\) 在主串中出現了多少次。
還是先把整個詢問串拿到主串的\(\text{SAM}\) 上跑一遍,記錄\(mx[i]\) 的同時記錄\(pos[i]\) 表示詢問串匹配的右端點是\(i\) 時,在自動機上的位置。對於詢問\([l,r]\) ,如果\(mx[r]<r-l+1\) ,答案為\(0\) 。否則需要在\(\text{parent}\) 樹上倍增找到最頂端的\(i\) 滿足\(len[i]\ge r-l+1\) 。此時的\(sze[i]\) 就是答案。
7、給定一個字串,求該字串所有僅出現了一次的子串
先建出\(\text{SAM}\) ,很明顯只出現了一次的子串一定是\(\text{parent}\) 樹上的葉子節點,他們的\(\text{endpos}\) 集合大小必定為\(1\) 。記錄\(end[i]\) 表示自動機上點\(i\) 對應的子串的任意一個結束位置,那麼對於葉子節點\(i\) ,它對應的子串的出現位置就是\([end[i]-len[i]+1,end[i]]\) 。
8、給定字串,多組詢問。每次給定該字串的若干字尾,求兩兩\(\text{LCP}\) 長度。\(\sum t\leq 3\cdot 10^6\)
首先兩個字尾的\(\text{LCP}\) 長度就是它們在反串的\(\text{parent}\) 樹上\(\text{LCA}\) 的\(len\) 值。那多個字尾兩兩\(\text{LCP}\) 長度就可以通過樹形\(\text{DP}\) 來求。又因為\(\sum t\leq 3\cdot 10^6\) ,所以可以建虛樹,在虛樹上\(\text{DP}\) 就行了。
9、給定一個主串和多個詢問串,求詢問串的每個字首在主串的\([l,r]\) 區間內出現了多少次。\(\sum t\leq 3\cdot 10^6\)
對主串建出\(\text{SAM}\) 後利用線段樹合併維護每個點的\(\text{endpos}\) 集合。同時拿詢問串在主串上面匹配,若匹配到了詢問串的第\(i\) 個字元,當前在自動機上的點\(j\) ,那麼在點\(j\) 的線段樹上詢問一下在\([l,r]\) 內的值即可。
10、給定一個主串和多個詢問串,求詢問串所有本質不同的迴圈同構串中,每個串在主串的出現次數
每個詢問串倍長接在後邊,然後在主串的\(\text{SAM}\) 裡跑匹配。如果沒有本質不同的要求,按照6 的做法即可。有了這個要求,就在找到的最淺的滿足要求的點打個標記,如果該點打過標記就不計入答案。
下面是廣義\(\text{SAM}\) 。
11、給定n個字串,求每個字串有多少個子串是至少k個串的子串
在\(\text{SAM}\) 上每個狀態開\(\text{set}\) 記錄這個狀態是哪些串的子串。
然後對於每個字串在廣義\(\text{SAM}\) 上跑匹配,如果匹配到的點的出現次數\(<k\) ,那就暴力跳\(\text{father}\) 直到出現次數\(\ge k\) 。那當前節點的\(len\) 值就需要被加進答案裡。本質上統計的是對於每個\(x\) ,每個串的所有以\(x\) 為結束點的子串的個數。
12、給定n個字串,對於每個字串求有多少子串滿足該子串沒有在其它任何一個字串中出現
建廣義\(\text{SAM}\) ,每個節點打標記是在哪個串出現。如果有多個標記設定為\(-1\) 。然後一路拓撲即可。因為在\(\text{parent}\) 樹上一個點的標記是\(-1\) ,那麼它所有祖先的標記一定都是\(-1\) 。