標籤:man 專題 根據 編號 首碼樹 結束 search class text
尾碼自動機練習專題一些比較有用的東東:
(1) \(\text{sam}\) 上一條從初始狀態出發的路徑對應一個子串
(2) \(\text{parent}\) 樹上一個節點能表示的最長的串對應一個首碼/尾碼
(3) \(len(u)\) 表示節點 \(u\) 能表示的最長串的長度
(4) \(fa(u)\) 表示節點 \(u\) 的尾碼連結指向的節點,也就是其在 \(\text{parent}\) 樹上的父親
(5) 表示兩個尾碼的公用首碼的節點是兩個尾碼在 \(\text{parent}\) 樹上的 \(lca\)
(6) \(R(u)\) 表示節點 \(u\) 的 \(\text{right}\) 集合,\(sz(u)\) 表示 \(R(u)\) 的大小,\(R(u) \subset R(fa(u)), sz(u) < sz(fa(u))\)
(7) 廣義 \(\text{sam}\) 可以理解為一個 \(\text{sam}\) 維護多個串,每一次插入完一個串後將 \(tail\) 指標設為初始狀態
SPOJ 1811 Longest Common Substring
給出兩個串 \(s, t\),求這 \(s, t\) 的最長公用子串,\(|s|, |t| \leq 2.5 \times 10^5\)
? 對於 \(s\) 建 \(\text{sam}\) ,根據 (1) 可以將 \(t\) 放進 \(\text{sam}\) 匹配,當失配時相當於要要從當前匹配到的串的一個尾碼繼續開始匹配,等價於在 \(\text{sam}\) 上跳 \(fa(u)\) 。可以證明這樣一定能匹配到最長公用子串。
SPOJ 1812 Longest Common Substring II
給出 \(n\) 個串,求這個 \(n\) 個串的最長公用子串,\(1 \leq n \leq 10, |s| \leq 10^5\)
? 和上題類似的, 還是取出一個串建 \(\text{sam}\),可以求出每一個節點被多少個串匹配到了,那麼答案就是 \(\max(len(u))\),\(u\) 是被 \(n - 1\) 個串匹配到的節點。考慮每一次匹配到某個節點的時候,根據 (6),其祖先代表的串也會被匹配到,所以每一個串匹配完以後還要更新其祖先的匹配次數,暴力向上跳即可。
? 另外一種做法,考慮串數只有 \(10\) ,可以建一個廣義 \(\text{sam}\) 將所有串放進去,用二進位維護 \(R(u)\) 是否包含來自某個串的尾碼,當一個節點包含來自所有串的尾碼時,其就可以對答案產生貢獻。當 \(n\) 比較大的情況可以用 \(\text{bitset}\) 或者線段樹合并來維護。
SPOJ 7258 Lexicographical Substring Search
給出一個串 \(s\),以及\(q\) 次詢問,每次詢問 \(s\) 中第 \(k\) 小的子串,重複的子串僅算一次, \(|s| \leq 9 \times 10^4, q \leq 500\)
? 可以不考慮 \(\text{parent}\) 樹,只考慮 (1) ,計算出每一個節點向下走能走到的路徑數量。由於要求的是字典序第 \(k\) 小,每一次二分出從這個節點出發應該走的轉移邊的字元是什麼,向下走即可。這樣做複雜度是 \(O(|s|q)\),由於 \(q\) 不大,足以通過此題。
? 實際上考慮 \(\text{parent}\) 可以進一步最佳化演算法的複雜度,考慮原先的 \(\text{parent}\) 樹一個節點代表的多個串都是最長的串的一個尾碼,是一棵類似於首碼樹的結構,這樣不能適用於一些字典序上優美的性質。不妨將串反序插入到\(\text{sam}\) 中,這樣每一個點能代表的多個串都是最長的串的首碼,這些串從長到短在字典序上一定是有序的。擴充到整棵樹上,根據 \(mn(u) = len(fa(u)) + 1\) ,每個點代表的字串都比其祖先代表的字串的字典序大。於是可以計算出每一棵子樹代表了多少串,在 \(\text{dfn}\) 序上二分答案即可,複雜度是 \(O(qlogn)\)。
「TJOI2015」弦論
給出一個串 \(s\) ,根據題目要求來求出相同子串算一個或多個的第 \(k\) 小子串 \(|s| \leq 5 \times 10^5\)
? 第一問和上一題完全等價,考慮第二問的情況,一個子串在 \(s\) 中的出現次數就是其對應 \(\text{parent}\) 樹節點的 \(\text{right}\) 集合大小,也就是 \(sz(u)\) ,只需要對於每一條路徑把權值為 \(sz(u)\) 進行統計即可。
Codechef January Challenge 2018 - Killjee and k-th letter
給出一個的串 \(s\),將 \(s\) 所有子串按照字典序排列好相接起來形成一個新串,\(q\) 次詢問,每一次詢問問新串中的第 \(k\) 個字元是什麼,強制線上。\(|s|, q \leq 2 \times 10^5\)
? 考慮上上題給基於 \(\text{parent}\) 的做法,反序插入後每個節點代表的字串在 \(\text{dfn}\) 序上是有序的,所以只要求出每一個節點代表的串的數量,然後對 \(\text{dfn}\) 序進行二分求出答案在哪個節點代表的子串上。考慮一個子串其所代表的串長度是從 \([len(fa(u))+1,len(u)]\) 連續的,可以直接求出 \(k\) 對應的子串是從節點 \(u\) 對應的尾碼的多少位,尋找該位置在原串上的字元即可
Codeforces 873 F. Forbidden Indices
有一個串 \(s\),\(s\) 的有些位置是非法的,求所有不以非法位置為結尾的子串 \(a\) ,\(\sum|a|\times tot(a)\) 的值,其中 \(tot(a)\) 表示 \(a\) 在 \(s\) 中的出現次數。\(|s| \leq 2 \times 10^5\)
? \(\text{parent}\) 樹上一個節點代表的串的總數就是 \((len(u)-len(fa(u))) \times sz(u)\) ,維護出每一個串不含非法位置的 \(\text{right}\) 集合大小後直接求和
「AHOI2013」差異
給出一個串 \(s\) ,對於其所有尾碼 \(t_i, t_j\) ,求 \(\sum len(t_i)+len(t_j) - 2len(lcp(t_i, t_j))\) 的值, \(|s| \leq 5 \times 10^5\)
? 考慮將串反序插入到 \(\text{parent}\) 樹後,兩個尾碼節點的 \(\text{lcp}\) 就是他們的 \(\text{lca}\) ,那麼可以樹形 \(\text{dp}\) 出以每一個節點作為 \(\text{lca}\) 時得到的 \(\sum len(t_i)+len(t_j) - 2len(u)\) ,最後對所有節點求和就是答案
「NOI2015」品酒大會
給出一個串 \(s\) 和一個序列 \(a\) ,對於所有 \(i\) ,求出 \(lcp(x, y) \geq i\) 的方案數以及滿足條件的最大的 \(a_x \times a_y\) \(|s| \leq 3 \times 10^5|a_i| \leq 10^9\)
? 本質上和上一題做法一樣,考慮第一問只需要求出對於每一個節點 \(u\) ,在其子樹裡選出兩個節點 \(\text{lca}\) 為 \(u\) 的方案數即可,第二問稍有複雜,因為權值可正可負,需要分類討論 \(\text{dp}\) 維護最大值和最小值,便於合并相乘時統計答案
「BZOJ3473」字串 (雙倍經驗=3277)
給出 \(n\) 個字串,求有多少個子串是其中至少 \(k\) 個字串的子串,\(\sum|s| \leq 10^5, 1\leq k \leq n\)
? 建立一個廣義 \(\text{sam}\) ,每次插入一個串後維護一下 \(\text{parent}\) 樹上哪些節點的 \(\text{right}\) 集合已經擁有了來自這個串的尾碼,這樣每一個新增的插入節點都需要向祖先更新這個資訊,根據均值不等式分析,插入所有串的總複雜度是 \(O(|s|\sqrt|s|)\)。
? 考慮可以直接用線段樹合并維護每一個節點的 \(\text{right}?\) 集合有哪些串的尾碼,全部建完以後向上合并更新祖先的答案,總複雜度 \(O(|s|log|s|)?\)。
「ZJOI2015」諸神眷顧的幻想鄉
有一棵 \(n\) 個節點的樹,每個節點上有一個字元,現在把每一條簡單路徑看成一個串,求本質不同的子串數量,\(n \leq 10^5\) 樹的葉子節點數量 \(\leq 10\)
? 如果一條簡單路徑被另外一條簡單路徑包含,那麼其代表的串完全可以看成另外一個串的子串,所以我們只需要考慮不被任何串包含的簡單路徑,其數量顯然是葉子節點個數的平方。於是暴力將這些串插入到廣義 \(\text{sam}\) 中對不同子串數量計數即可。
「SDOI2016」產生魔咒
一開始有一個空串,一共有 \(n\) 次操作,每一次操作在這個串末尾加一個字元,求每一次操作結束時串中不同子串的數量 \(n \leq 10^5\)
? \(\text{sam}\) 是線上構造的,每一次插入節點 \(p\) 後維護一下 \(len\) 發生改變的節點對答案的貢獻即可
「BZOJ2555」 Substring
在當前字串的後面插入一個字串
詢問字串 \(s\) 在當前字串中出現了幾次?(作為連續子串) 你必須線上支援這些操作。
? 喪心病狂的一道題,由於線上插入串到 \(s\) 後還要維護每一個點的 \(sz(u)\) ,所以要動態支援給 \(\text{parent}\) 樹加邊,維護子樹大小可以轉為鏈加,用 \(lct\) 來維護即可
Codeforces 666 E. Forensic Examination
給你一個母串和很多詢問串,每次詢問求母串的一段區間在一段連續詢問串中出現最多的詢問串的出現次數和編號 \(1 \leq |s|, q \leq 5 \times 10^5\)
? 對所有串建一個廣義 \(\text{sam}\) ,每次詢問倍增找到左端點在 \(\text{parent}\) 樹上對應的節點,用線段樹合并維護出每個節點的 \(\text{right}\) 集合中每個詢問串的出現次數,區間查詢 \(\max\) 即可
「TJOI / HEOI2016」字串
? 傳送門
「NOI2018」你的名字 (非正解)
? 傳送門
「BJWC2018」 Border的四種求法 (待填坑)
?
尾碼自動機練習專題