“寫sam是肯定會去寫的,這樣才學的了字符串,后綴數組又不會用 >ω<,
sam套上數據結構的感覺就像回家一樣!
里面又能剖分又能線段樹合並,調試又好調,我愛死這種寫法了 !qwq”
SAM是一個DFA,它存儲了某字符串的所有子串信息。
待更。
博主水平不行,盡量在退役前多更些。
插入字符:
1 void extend(int id,int&now) 2 { 3 int p=now; 4 if(ch[p][id] && len[ch[p][id]]==len[p]+1) 5 { 6 now=ch[p][id]; 7 return; 8 } 9 int np=++tot; 10 len[np]=len[p]+1; 11 while(p && !ch[p][id]) 12 { 13 ch[p][id]=np; 14 p=fa[p]; 15 } 16 if(!p) 17 { 18 fa[np]=1; 19 } 20 else 21 { 22 int q=ch[p][id]; 23 if(len[q]==len[p]+1) 24 { 25 fa[np]=q; 26 } 27 else 28 { 29 int nq=++tot; 30 len[nq]=len[p]+1; 31 fa[nq]=fa[q]; 32 for(int i=0;i<26;i++) 33 ch[nq][i]=ch[q][i]; 34 fa[q]=fa[np]=nq; 35 while(p && ch[p][id]==q) 36 { 37 ch[p][id]=nq; 38 p=fa[p]; 39 } 40 } 41 } 42 now=np; 43 }
套路:
每條路徑與子串一一對應。
$len_i$:該節點最長串的長度
$Parent Tree$上的子樹和:串的出現次數
$len_i - len_{fa_i}$:該節點代表的串的數目
部分題目選:
(1)[SAM入門題][NOI2018]你的名字
線段樹合並維護$right$集合
(2)[SAM入門題][CTSC2012]熟悉的文章
$SAM$只是用來預處理的,求答案還要靠二分+單隊$DP$
(3)[SAM入門題][bzoj2555]Substring
$LCT$對$Parent Tree$結構的維護
(4)[bzoj3277]字符串
廣義后綴自動機的應用
暴力搞復雜度大概是根號的,不會證明。
(5)[bzoj5084]HASHIT
帶撤銷后綴自動機,隨機數據隨便艹。
upd:BFS建樹形后綴樹以后再進行DFS+樹鏈求並統計答案就行了。
(6)[USACO]牛奶模式
后綴自動機的基本原理與使用
(7)[洛谷元旦賽]WD與數列
維護$right$集合的好題(似乎還要在parent樹上跑啟發式合並計算貢獻,我並不會)
現在可能會了。
(8)[FZOJ2258]封印
校內題目,沒連接
大概就是你知道$Parent$樹是干啥的,形態如何,就能直接切掉。
(9)[TJOI2016]字符串
考慮求出 sam 上一個串 p 在 [l,r] 里出現的次數,這個可以通過數一數 p 的 right 集合里有多少在 $[l+len_p-1,r]$ 中出現。
lcp 可以二分,倍增可以從區間 locate 子串,那么這個題就 $O(n\log^2 n)$ 了。
upd:話說為啥沒見過把狀態設在sam節點上的DP題?
upd:CF700E,已經單獨更新
(10)[十二省聯考]字符串問題
題目意思大概是描述給你一個連邊關系,求 DAG 上最長路。
直接暴力構圖可以 $n^2$ ,用 hash 判斷。
如果考慮建后綴樹,那么在一個節點上有短->長的后綴關系,另外父子關系也是后綴關系,拆點據此連邊即可。
只需要實現對區間 locate 子串即可,時間復雜度 $O(n\log n)$。