在多個串的字符串題中,每每會出現一類題須要用到某個子串是否在一些母串中出現。此時對於 \(\text{parent}\) 樹的 \(\text{right}\) 集合而言,問題並不關心某個具體位置而只關心是否有某個 \(\text{endpos}\) 在指定母串中。數據結構
那麼對於 \(\text{parent}\) 樹上的來自同一個母串的節點而言,其對祖先的貢獻都是能夠替代的,並不須要重複標記其某個祖先 \(\text{right}\) 集合中是否存在一個 \(\text{endpos}\) 來自這個母串。測試
因而咱們維護來自這個母串的全部節點對 \(\text{parent}\) 樹的貢獻複雜度等價於全部來自這個母串的葉子節點在 \(\text{parent}\) 樹上的並的大小。(此處葉子指的是來自這個母串且其子樹內不存在來自這個母串節點的節點)spa
分析這些葉子在樹上的並的大小的具體規模,有一個顯然的上界就是 \(\sum_{i=1}^{n}2len_i-1\) ,其中 \(len_i\) 是第 \(i\) 個串的長度,對其建後綴自動機的節點數上界爲 \(2len_i-1\) ,對 \(n\) 個串一塊兒建能夠獲得這個顯然的上界。字符串
觀察這棵並起來的樹的性質能夠發現,這棵樹上的每個節點的 \(\text{right}\) 集合都包含來自這個母串的 \(\text{endpos}\) ,而每一個節點的父節點都是該節點所表明串的一個後綴且至少長度減小 \(1\) 。那麼考慮這個串每個前綴的最大貢獻就是這個前綴的長度,因此能夠獲得另一個上界 \(len_i^2\) 。模板
假設這 \(n\) 個串的總長爲 \(S\) ,那麼 \(\sum_{i=1}^{n}2len_i-1\) 能夠看作 \(2S\) ,因此對於每個母串都維護貢獻的複雜度是 \(\sum_{i=1}^n \min(2S, len_i^2)\) ,且 \(\sum_{i=1}^n len_i = S\) 。根據均值不等式能夠獲得 \(n\) 和 \(len_i\) 都取 \(\sqrt{2S}\) 時達到上界,總複雜度 \(O(S\sqrt{2S})\) 。class
然而事實上後面那個下界不容易卡滿,由於你須要構造一個字符串其每個前綴都能在 \(\text{parent}\) 樹上分支出最長的一條鏈,因此這個根號實際上跑起來比某些大常數的一個 \(log\) 作法還快。效率
BZOJ3277 字符串遍歷
題意:給出 \(n\) 個字符串,對於每個字符串求出其有多少個子串在至少 \(k\) 個字符串中出現過。統計
能夠說是這個特技的模板題了,不過線段樹合併能夠作 \(O(nlogn)\) ,這裏就只說根號作法吧。建出後綴自動機後枚舉每個串的葉子節點暴力往上跳給祖先節點出如今不一樣的母串的次數 \(+1\) 便可,已經被別的該串節點遍歷過就跳出,複雜度就是上述的 \(O(S\sqrt{2S})\) 。數據
NOI2018 你的名字
題意:對於每個詢問串,求出其有多少個不一樣子串在母串的 \([L, R]\) 之間出現過。
將問題轉化爲求有多少個子串在詢問串和母串的 \([L, R]\) 之間共同出現便可,暴力枚舉的同時額外加一個線段樹合併判是否在 \([L, R]\) 便可,複雜度 \(O(S\sqrt{2S}logS)\) 。不過套上 \(log\) 之後會由於第二個上界被卡高後被叉掉,可是 \(68pts\) 不須要線段樹合併判斷,能夠確保經過。實際上3s內跑過了全部測試點
Sum of Squares of the Occurrence Counts 增強版
沒增強以前這個特技不能作
題意:給出 \(n\) 個串,對於每個串 \(i\) 求出其全部子串在串 \([1,i]\) 之間的出現次數的平方和。
對於全部串建後綴自動機,用線段樹合併維護出 \(\text{parent}\) 樹上每個節點擁有來自哪幾種母串的 \(\text{endpos}\) ,以及每一種母串對應的 \(\text{endpos}\) 數量。考慮每個母串對全部線段樹大小之和的貢獻就是葉子的並,因而暴力遍歷合併後的每一棵線段樹的總複雜度就是 \(O(S\sqrt{2S})\) 。直接在線段樹上暴力統計每個 \(\text{parent}\) 樹上節點對每個串的答案的貢獻便可,總複雜度 \(O(S\sqrt{2S}+SlogS)\)。
若是你想知道會了這個根號特技有什麼用,我也說不清。對於我這種字符串菜雞來講,寫簡單好寫的作法比套上各類數據結構好調多了,這個特技犧牲了一些時間效率,可是大大簡化了思惟難度和代碼難度。固然若是您是神仙徹底能夠去秒正解。