SAM初學筆記

2022-05-11 13:16:05 字數 1385 閱讀 1663

我第一次學習sam,可能有很多地方理解有偏差或者錯誤。還請各位大佬指正。這篇文章中沒有嚴謹的證明,只有感性的理解。追求嚴謹的請轉他處。

首先我們定義parent樹:乙個節點與其所代表的字串的最長的,且出現次數與其不一樣的字尾連邊,所形成的樹是parent樹。

可以發現parent樹有很多優秀的性質,比如子樹內的節點數即為這個串出現次數等。

我們基本的sam要維護三個值:父親,當前點長度,兒子。注意這裡的兒子是廣義上的兒子,即這個兒子所代表的不僅是父親為它的節點,而是所有他有出邊的節點。因為sam是一張圖。

sam有乙個性質,就是從源點到所有點的所有路徑,就是所有的子串。

如何構建sam呢?

如果我們要往sam中加入乙個字元\(c\),先處理好\(len\),我們從上一次插入的地方開始將它沿parent樹往上跳。直到跳到有乙個節點有乙個兒子也是\(c\)

在這個跳的過程中,我們將一路上的節點都新增乙個兒子\(c\),這個怎麼理解呢?就是將這些字尾末尾都增加乙個\(c\),而為什麼只有這一路要加呢?因為剩下的均不是出現次數與其不一樣的字尾。

然後我們找到這個節點\(p\)與其兒子\(q\),這時分類討論:

如果\(len_q=len_p+1\),那麼\(p\)也為當前的總串\(s\)的字尾,所以將\(c\)指向\(p\)

否則我們發現字串集\(p\)中有乙個是總串\(s\)的字尾,而其他不是。為了避免混淆而引起的錯誤,我們可以把\(p\)拆開,分出乙個\(g\),同時\(g\)的兒子資訊與\(q\)的一樣。因為根據定義,\(g\)也是\(q\)的字尾,所以後面加上乙個是不會改變的。

但是這時我們把它拆開了不就會少掉嗎?所以我們把\(q\)指向\(g\),\(g\)指向\(p\),同時把上面所有的兒子為\(p\)的指向\(g\),如果不這樣同樣會少掉答案。而關於所有兒子為\(p\)的節點肯定是連續的,因為肯定是乙個字元加入是不斷往上跳改變出來的。

而此時\(g\)就滿足了\(len_g=len_p+1\),那麼就可以把\(c\)接在\(g\)後面了。

不得不說整個sam還是很巧妙的。

放一下模板題的code:

#include#include#define ll long long

#define max(a,b) ((a)>(b)?(a):(b))

using namespace std;

int n,m,k,x,y,z,cnt=1,last=1,now,p,cur,pus,g[2000039],w[2000039],dp[2000039],ans;

char s[1000039];

struct samf[2000039];

inline void insert(int x)

} else f[now].link=1;

}int main()

SAM 學習筆記

詳見 text 字串 s 的 text 是乙個接受 s 所有字尾的最小dfa 確定性有限自動機或確定性有限狀態自動機 其中,s 每個字尾均可用一條從初始狀態 t 到某個終止狀態的路徑構成。包含 s 的所有子串 從 t 開始的任意路徑都構成乙個子串,每個子串也對應某條路徑。但是到某個狀態的路徑可能不止...

學習筆記 SAM

不想學博弈論不想學 sa 不想學插頭 dp,學 lct 被 axdea d 飛了,那就來學 sam。sam 是字尾自動機,名義上是字尾,但實際上它能表示出乙個字串的所有不同子串。不同於你的 o n 2 列舉,sam 構造,節點和邊的數量也都是 o n 級別的。更具體的,sam 表現為一張 dag,每...

字尾自動機 SAM 學習筆記

參考資料 hihocoder1441 hihocoder1445 史上最通俗的字尾自動機詳解 練習題hihocoder1449 hihocoder1457 hihocoder1465 hihocoder1413 筆記 字串 aab 模板struct suffixautomaton int q ch ...