字尾陣列題目選講
字尾陣列題目選講
複習題:luogu題單
CNOI的題
\(1.\)NOI2015 品酒大會
題意:\(\forall i∈[0,n)\)求有多少對字尾滿足\(lcp \ge i\),以及滿足條件的兩個字尾的權值乘積的最大值。
我們統計出對於\(1...n\)中的每個\(i\)統計一下有多少個\(lcp=i\)再做個字尾和。
因為\(lcp(i,j)=min_{i<k \le j}{heiht_k}\),我們考慮每個\(heiht_k\)能產生的貢獻是一段區間。
這個是個單調棧經典問題,求出包含\(heigt_k\)且\(heigt_k\)是最大值的最長區間,然後左右長度相乘即可。
而最值可以用\(st\)
\(2.\)NOI2016 優秀的拆分
計算出\(st_i\)表示以\(i\)開頭的\(AA\)串個數,\(ed_i\)表示以\(i\)結尾的\(AA\)串個數,我們用\(hash\)可以\(O(1)\)判斷一個子串是否為\(AA\)串,答案就是\(\sum_{i=1}^{n-1}st_{i+1}\times ed_i\),複雜度\(O(n^2)\),這樣就可以拿到\(95pts\)了,最後5pts打表就行了。
考慮如何更快的求出\(st\)和\(ed\)
設\(lcp(i,j)\)為以\(i,j\)字尾開頭的最長公共字首,\(lcs(i,j)\)為以\(i,j\)字尾結束的最長公共字尾,這個可以建正反串字尾陣列求出。考慮兩個相鄰的關鍵點\(i,j\),其中\(i+l=j\) ,那麼顯然當\(lcp(i,j)+lcs(i,j) \ge L\)時產生了貢獻,貢獻為\(lcp(i,j)+lcs(i,j)-L+1\)
如圖紅色是\(i,j\),藍色和綠色分別是\(lcp\)和\(lcs\),其中粉色是\(AA\)串,而這個\(AA\)串可以一直向後滑動到棕色部分,滑動中經過的串的個數就是\(lcp(i,j)+lcs(i,j)-L+1\),所以我們把紅色加粗部分的所有\(st_i\)加\(1\),把綠色加粗部分的所有\(ed_i\)加\(1\),這個用差分實現即可。
複雜度:\(O(nlog_2n+n+\frac n 2+\frac n 3+...+1) \approx O(n \log_2 n)\)
\(3\).NOI2018 你的名字
題意: 給你一個字串\(S\), 多次詢問給定一個\(T\), 求\(T\)中不在\(S[l...r]\)中出現的本質不同的子串個數。
老套路,把詢問離線,把所有串用一個特殊字元連在一起做字尾陣列。
我們按順序列舉\(T\)的每個字尾\(i\),我們求出最大的長度\(L\)使得\(\large T[i,i+L-1]\)是\(S[l...r]\)的子串,那麼長度小於\(L\)的顯然都不滿足條件,可以直接計算。這個\(L\)是可以二分的,但是我們發現所有的\(i+L-1\)這個位置隨著\(i\)的增大是單調上升的,所以雙指標即可。
所以現在只要判斷一個\(L\)是否可行。事實上就是查詢\(S[l...r-L+1]\)中是否存在一個\(k\)使得\(lcp(k,i) \ge L\),我們先求出滿足這個條件的\(k\)在後綴陣列上的區間\([Le,Ri]\),然後判斷\([Le,Ri]\)中是否存在一個字尾\(k \in [l,r-L+1]\),使用主席樹查詢即可。
用本質不同子串個數減去上述得到的答案即可。
時間複雜度\(O(nlog_2n)\),注意常數。
\(4.\)[HEOI2016/TJOI2016 字串
考慮二分答案,二分一個\(L\)只需要判斷是否存在一個\(k \in [b-L+1,b]\)使得\(lcp(k,c)=L\),根據上題的做法,先二分出在後綴陣列的對應的區間\([Le,Ri]\),然後主席樹查詢即可。
時間複雜度\(O(nlog_2^2n)\)
CF的題
\(5.\)CF504E
先樹剖再按\(dfn\)序建立正反字尾陣列,跳重鏈的時候記錄下重鏈的左右端點,然後拼起來。
每次我們用\(st\)表求出\(lcp(i,j)\)然後根據長度判斷是否還能向擴充套件。
因為兩個點之間重鏈個數最多隻有\(2log_2n\)個,所以我們最多比較\(4log_2n\)次。
這個做法常數很小,最慢的點只要2s,目前我的賬號周小涵是\(luogu\)的最優解。
\(6\).CF666E
老套路,把所有串用一個特殊字元連在一起做字尾陣列。
容易發現答案就是對於所有滿足\(lcp(pl,k) \ge pr-pl+1\)中\(col_k \in [l,r]\)的眾數。
我們考慮將詢問按 \(pr-pl+1\) 從大到小排序,再把\(heiht_i\) 也從大到小排序,用兩個指標,一個指向當前詢問,一個指向當前的\(heiht_i\),我們將所有\(height_i \ge pr-pl+1\)的進行合併操作。也就是把\(i\)和\(i-1\)所在的集合合併,用資料結構維護眾數即可,很顯然用並查集+線段樹合併可以很好的維護這個資訊。這題的做法和noip2013 貨車運輸的思想是一樣的,所以事實上也可以用\(kruskal\)重構樹維護眾數。
\(7.\)CF1037H
我們列舉答案與\(T\)的\(lcp\)長度\(L\),找出在後綴陣列上對應的區間\([l,r]\),因為新增一個字元只會使區間縮小,所以我們用兩次二分可以確定新一輪\([l,r]\)。
我們再枚第\(L+1\)個應該填什麼字元,這個字元應滿足字典序是大於原字串的對應位置的字元,按照上述同樣的方法求出\([l,r]\),然後判斷\([l,r]\)中是否\(\exist~sa_k\in[Le,Ri-L]\),這個用主席樹維護即可。