1. 程式人生 > >Sam做題記錄

Sam做題記錄

Hihocoder 字尾自動機二·重複旋律5

求一個串中本質不同的子串數

顯然,答案是 \(\sum len[i]-len[fa[i]]\)

Hihocoder 字尾自動機三·重複旋律6

求一個串每個長度出現次數的最大值

求出fail樹每個點的size就是該點的出現次數,由於答案是(非嚴格)單調減的,每個點更新一下 \(ans[len[i]]\)

Hihocoder 字尾自動機四·重複旋律7

求幾個數字串本質不同的子串所代表的數的和, \(mod\ 10^9+7\)

先建一下廣義sam,在轉移邊上bfs更新答案

Hihocoder 字尾自動機五·重複旋律8

給定一個字串S,詢問另一個字串T的子串和S的子串匹配數。匹配的定義為兩個串迴圈同構。

對S建Sam,把T倍長之後在Sam上跑,如果一個位置匹配長度大於|T|,那麼沿著fail樹向上跳到 \(len[i]>=|T|\) 的最小的 \(len[i]\) ,如果沒有被更新過那麼更新答案

[USACO17DEC] Standing Out from the Herd

給定多個字串,求每個字串只屬於他自己的本質不同的子串數

建廣義Sam,如果一個狀態他在多個字串中出現,那麼他沒有貢獻,否則給他所在的字串貢獻 \(len[i]-len[fa[i]]\)

「20180714NOI模擬」Ernd

給定一個長度為 \(n\) 且僅包含小寫英文字母的字串 \(S\)。你有一個字串 \(T\),初始為空串。

你可以進行 \(n\) 次操作,每次操作你可以在 \(T\) 的前端或末尾加入一個任意字母。記第 \(i\) 次操作後 \(T\)\(S\) 中的出現次數為 \(f_i\),你需要最大化 \(ans =\sum_{i}^{} f_i\)

對S建Sam,在末尾加入字母就是沿著匹配邊走,在前面加字母就是在fail樹往下走,直接把這些邊全連上跑拓撲就行了

[ZJOI2015]諸神眷顧的幻想鄉

給定一棵無根樹,每個點有一個字元,求本質不同的子串數,葉子節點不超過20個

注意到最後那個條件,直接每個葉子拎出來,插到sam裡,然後同第一題

BZOJ2894 世界線

給定一棵Trie,求不同子串數和第K小子串

建廣義Sam,第一問不說了,第二問先求出每個點沿著轉移邊能走多少條路,然後按位貪心

[HEOI2015]最短不公共子串

給兩個小寫字母串A,B,請你計算:

(1) A的一個最短的子串,它不是B的子串

(2) A的一個最短的子串,它不是B的子序列

(3) A的一個最短的子序列,它不是B的子串

(4) A的一個最短的子序列,它不是B的子序列

建Sam和序列自動機(就是求出 \(next[i][c]\) 陣列, 表示第 \(i\) 個位置下一個字元 \(c\) 的位置)

然後幾個詢問都是在某兩個自動機上跑BFS

[Feyat cup 1.5]Str

求兩個字串的最長公共字串,兩個字串相等的條件是它們至多有一個位置不同

考慮暴力:列舉兩個字串不同的位置 \(i, j\) ,那麼答案為 \(lcs(a_{1\dots i - 1},b_{1\dots j -1}) + 1 + lcp(a_{i + 1 \dots |a|},b_{j+1\dots|b|})\)

假設我們知道 \(i, j\) ,那麼這個值可以在 SAM 或者 SA 上查出來 (把兩個串連一下,正反各建一個即可)

考慮優化這個列舉的過程

根據 SA,如果我們知道如果 \(i, j\)\(rank\) 越接近,那麼它的 LCP 就越大,那麼可以想到用set維護一下這個東西

LCS是什麼東西?兩個點在 SAM 上的對應節點的在 $ parent $ 樹上的 LCA 的 \(len\) 就是它們的 LCS!

因此,在 \(parent\) 樹上啟發式合併,然後像啟發式那樣去查就行了

複雜度 \(O(nlog^2n)\) 好像可以一個 \(log\) 不過我還暫時不會。。

[HAOI2016]找相同字元

給定兩個字串,求出在兩個字串中各取出一個子串使得這兩個子串相同的方案數

就兩個串,建一下廣義 SAM ,然後對每個串記錄一下每個節點的 \(size\) 乘一下加起來即可

[HEOI2016/TJOI2016]字串

給定字串 \(S\) ,多次詢問子串s[a..b]的所有子串和s[c..d]的最長公共字首的長度的最大值

首先反著建 SAM,\(lcp\) 就轉化為 \(lcs\) ,二分答案 \(ans\),然後就轉化為問 \(endpos\)\(c\)\(len \ge ans\) 的節點的 \(endpos\) 集合中是否包含 \(a\dots b - ans+1\) 的某個值,線段樹合併求 \(endpos\) 然後查一下就行了。注意每次詢問還要找到最小的 \(len \ge ans\) 的節點,在 \(parent\) 樹上倍增即可

[CTSC2012]熟悉的文章

給一堆01模板串,然後詢問把一個另外的串 \(S\) 分割成若干段,長度大於等於 \(L\) 的且在模板串裡出現過的子串長度總和不小於 \(|S|\) ,求最大的 \(L\)

建廣義 \(SAM\), 把詢問串放在上面匹配,可以求出每個位置結尾的最大匹配長度,然後二分一個答案,然後單調佇列優化一下DP

具體地,\(f[i]\) 表示已經決策到 \(i\) ,目前的最大長度
\[ f[i]=\max_{i-j\le ans}f[j]+i-j \]

CC Killjee and k-th letter

給你一個字串 \(S\),定義它的生成串 \(T\)\(S\) 的所有子串(位置不同算多個)按照字典序排序後,依次串接形成的串,多次詢問 \(T_i\)

建出字尾樹,你發現字尾樹就是個 Trie,每條路徑都可以代表一個子串,按照出邊排序一下,就可以發現 直接在這個 Trie 上 DFS就可以得到正確的順序,所以按照 DFS 序處理一下,搞個字首和,查詢的時候計算計算即可

未完待續。。。。。