1. 程式人生 > 其它 >[Exercises on 2022.5.11] 字串轉風車煮飯吃蒸飯吃脂肪層

[Exercises on 2022.5.11] 字串轉風車煮飯吃蒸飯吃脂肪層

例 1. \(\text{CF504E Misha and LCP on Tree}\)

首先可以想到預處理從根到每個點和每個點到根的雜湊值(需要處理向上和向下的兩種情況),然後進行二分,用長鏈剖分 \(\mathcal O(1)\) 找祖先即可做到 \(\mathcal O((n+m)\cdot \log n)\)。或者對整棵樹重鏈剖分,維護 \(\rm dfs\) 序列上的雜湊值(當然之前的方法也可行),這樣路徑會被劃分成 \(\mathcal O(\log n)\) 條重鏈,對兩條路徑進行掃描,一直到不同的重鏈再進行二分,兩個 $\log $ 是分開的,總共是 \(\mathcal O(m\log n)\)

。後面的做法如果覺得求雜湊不保險的話可以換成 \(\rm sam\),只需要將每條重鏈向上向下兩種情況扔進 \(\rm sam\) 即可。


例 2. \(\text{CF204E Little Elephant and Strings}\)

一些閒話:我字串真的好垃圾啊!猛然發現自己只有字串模板題水平 o(TヘTo)。

考慮 \(\rm SA\)。將所有字串接在一起,中間的字元必須兩兩不同,然後做一個 \(\rm SA\)。固定左端點 \(l\),向右延伸找到恰好使得 排名\([l,r]\) 之中的字尾所屬字串種類為 \(k\)\(r\)。容易發現,這樣的 \(r\) 是遞增的,所以可以維護一個雙指標。對於每個尺取到的 \([l,r]\)

,我們找到排名在 \([l,r]\) 之間的字尾的 \(\rm lcp\),這就說明 \(\rm lcp\) 及其所有字首都是滿足題意的子串。

現在的問題是我們如何將滿足題意的子串貢獻到每個字串上,還是考慮 \([l,r]\) 是一段區間比較好維護,所以可以將貢獻累加在後綴上(因為我們是取 \(\rm max\),所以不存在算重的問題,令這個陣列為 \(f\)),用個隨便什麼東西維護區間取 \(\max\),單點查值都行。最後將字尾上的貢獻累加到字串上即可。

完了嗎?

事實上,上文的演算法並不完全正確。還是考慮尺取到的 \([l,r]\),我們只保證這是 "以每個 \(l\) 開始,恰好使得 排名

\([l,r]\) 之中的字尾所屬字串種類為 \(k\)",我們不繼續向右拓展基於一個貪心 —— 再向右 \(l\) 的答案只會更劣。但這同時也意味著某些排名 \(>r\) 的字尾沒有被貢獻。事實上,解決方案很簡單:

\[f_i=\max\{f_i,\min\{h_i,f_{i-1}\}\} \]

同時,我們也可以發現根本不需要維護區間取 \(\max\),只用更新 \(f_i\) 即可!

另外,向左拓展一定是不優的,這個很容易證明。再另外 \(k=1\) 的情況有些特殊,需要另行考慮。