【複習筆記】2021-12 字串
本文主要是記錄複習模板的情況
字尾自動機
-
計運算元串在多少個模板串中出現,可以建立廣義 SAM 後對於每一個模板串找到其所有字首在廣義 SAM 上的節點,然後從它們開始暴力跳 parent 樹算貢獻,並給每個訪問了的節點打上訪問標記保證在同一個模板串的計算過程中一個點不經過兩次的做法複雜度為 \(\Theta(n\sqrt n)\),證明考慮根號分治
-
先區別叫法:按照正序加入自動機再連線 \((i,fail_i)\) 得到的樹是 \(\rm{Parent\ Tree}\),而逆序加入得到的是字尾樹
-
在 \(\rm{Parent\ Tree}\)
-
在 \(\rm{Parent\ Tree}\) 上根鏈操作表示將這些字尾進行一次在當前最後一次出現位置上進行更新:出現次數的增加/更新最後一次出現位置
-
廣義 \(\rm{SAM}\) 的可能正確寫法是加入的時候判斷 \(\rm{las}\) 是否已經有這個兒子,也因此可以任意調整 \(\rm{las}\) 的值進行加入新點,可以應用於 \(trie\) 樹上建字尾自動機
-
用 \(\rm{LCT}\) 維護 \(\rm{SAM}\) 得到的 \(parent\)
基於是不是強制線上要求是不是需要寫
access
之外的函式,Luogu7361 是統一處理,可以離線,而另外的 \(\rm{Luogu}5212\) 不行,需要寫全套 -
使用 \(\rm{Parent\ Tree}\) 或者字尾樹維護字典序(例如求 \(\rm{SA}\))
對於樹上同一節點的子節點的代表元是 \(\rm{str[len[fa[x]]+pos[x]]}\),連邊的時候排序即可
直接求 SA 需要注意比較的是字首資訊,所以使用的是倒序建立的字尾樹
Luogu5115
可能的計算答案方式並不多,嘗試按照位置統計貢獻失敗之後轉行來按照每個字串來統計貢獻
觀察一下一個特定長度的字串會帶來多少的貢獻:
\[\sum_{i=1}^{len}(len-i+1)i[i\le k_1][len-i+1\le k2] \]當 \(len\leftarrow len+1\) 時,增量只有三種:新增 \(i=len+1\) 的貢獻/刪掉 \(i=len+1-k_1\) 的貢獻/\((len-i+1)i\) 的變化量,可以 \(\Theta(n)\) 遞推得到
剩下的問題就是每個字串在統計進答案的時候一定要保證是極長的,由於 \(endpos\) 的原因,在節點對應的字串前面加入相同字元不再可行,但是可以在後面加
注意到 \(SAM\) 每個節點可以維護出來對應在原字串中的後面一個字元,於是每次統計的時候用總的對數減去後繼相同的對數即可
時間複雜度 \(\Theta(n\Sigma)\)
Luogu7361
能離線就離線,所以掃描線,觀察新增最後一個節點會帶來哪些字串出現了第二次
首先在 \(\rm{LCT}\) 上面維護第一次/第二次出現的位置,此時每個 splay 上的點最後一次出現次數相同
不難發現在 access
的過程中,跳虛邊的父節點是能經過的鏈上的字串長最大值,又由於最後一次出現節點被更新成一樣的了,又因為是在同一個 splay 上,更新前視角下最後一次出現也是一樣的,所以它可以代表整個 splay 來更新
此時考察答案的形式:一個節點代表的字串被完全包含在區間裡面/被部分包含
如果完全包含直接用 \(len[x]\) 更新答案,否則用 \(\rm{last-left}\) 來更新
由於是掃描線,所以維護兩個線段樹維護兩種情況下最大值,每次跳虛邊時給能更新的更新即可
注意不能包含時對答案的貢獻一定是 \(\rm{rpos-queryl}\) 而每個葉子對應的 \(\rm{queryl}\) 一定,所以維護 \(\rm{rpos}\) 最值
Z-Function
Z 函式演算法本質是減少冗餘,儘可能利用已經計算過的資訊
在 if(i<=r) z[i]=min(r-i+1,z[i-l+1]);
一句中就很好體現了這點 ,這也恰好保證了整個處理過程複雜度
CF432D
主要是處理字首在字串中出現次數:
z[i]
計算的是每個字尾和全串的 \(\rm{LCP}\),那麼對於一個字首 \(S[1\dots l]\),滿足 \(z_j\ge l\) 的 \(j\) 為起始點,必然出現了一次
不難發現子串在母串出現必然有唯一起始點,所以該做法可以不重不漏統計
直接對 \(z_i\) 開桶然後字尾和即可
AC 自動機
- 對 Fail 樹根鏈更新表示更新該串子串資訊