字串 字尾自動機

2022-05-11 06:01:57 字數 2715 閱讀 3757

struct sam 

void extend(int c)

if(p == 0)

int q = nxt[p][c];

if(len[p] + 1 == len[q])

int v = ++siz;

mc(nxt[v], nxt[q]);

len[v] = len[p] + 1;

lnk[v] = lnk[q];

cnt[v] = 0;

while(p != 0 && nxt[p][c] == q)

lnk[q] = v;

lnk[u] = v;

}int c[2 * maxn];

int a[2 * maxn];

void calc()

printf("%lld\n", ans);

}} sam;

有唯一的乙個初始狀態,有乙個或多個終止狀態。

從初始狀態開始的任意一條由nxt組成的路徑,都是原串的乙個子串;原串的每乙個子串都對應一條這樣的路徑。

由初始狀態開始的到終止狀態的任意一條由nxt組成的路徑,都是原串的乙個字尾;原串的每乙個字尾都對應這樣的一條路徑。

也就是子串對應(從初始狀態開始的)路徑,狀態對應終止於這個狀態的路徑的集合。

endpos(t)字串s的子串t在s的字尾自動機上對應的所有終止位置(狀態)的集合。

兩個非空子串(u,v),設|u|<=|v|,假如擁有相同的endpos,當且僅當u是v的字尾,且每一次出現u都是以v的字尾形式存在的。

兩個非空子串,要麼endpos毫不相交,要麼其中乙個的endpos是另乙個的子集(在這種情況下,大的endpos集合對應的子串是小的endpos集合對應的子串的字尾)。

每個endpos集合對應一類子串。

字尾連線lnk組成一棵樹,滿足endpos(v)是endpos(lnk(v))的子集。

本質不同的子串的數量:

每個狀態對應的(本質不同的)子串數量是 len(v),每次去掉前面的乙個字元讓len-1就得到了乙個新的串。注意這個狀態的淨貢獻是len[u]-len[lnk[u]] ,也就是減去被lnk父親貢獻過的部分。

也可以不使用lnk,使用nxt進行動態規劃,使用nxt時,字尾自動機是乙個dag,直接寫乙個記憶化搜尋完事。

dp[u]表示從狀態u開始的路徑的數量。

\(dp[u]=1+\sum dp[v]\)

本質不同的字元的總長度:

每個狀態對應的(本質不同的)子串的總長度是 (len(v)+(len(v)+1))/2。也是要注意減去lnk父親貢獻過的部分。

也可以不使用lnk,使用nxt進行動態規劃,使用nxt時,字尾自動機是乙個dag,直接寫乙個記憶化搜尋完事。

dp1[u]表示從狀態u開始的路徑的數量,dp2[u]表示從狀態u開始的路徑的總長度,注意第二個轉移表示這裡面每乙個串的長度加起來是dp2[v],其中每乙個都變長了1,增加了dp1[v]。

\(dp1[u]=1+\sum dp1[v]\)

\(dp2[u]=\sum (dp2[v]+dp2[v])\)

最小迴圈移位:

字串s+s包含s的所有迴圈移位作為子串,對s+s構造字尾自動機,從初始狀態開始貪心選最小的字元進行轉移,然後轉移恰好|s|次就行。

某個子串的出現次數:

找到這個子串對應的endpos集合,輸出這個集合的大小。endpos集合不能顯式維護,所以只能找這個集合的大小。假如乙個節點不是複製建立的(也就是被插入建立的),則它的endpos大小為1,複製的狀態和初始狀態的endpos為0。然後cnt(lnk(v))+=cnt(v)把更長位置的出現次數疊加到短的狀態上。(上述**就是這個模板)

所有子串的出現次數:

在上面的「某個子串的出現次數」問題裡面,乙個狀態實際上對應著 len[u]-len[lnk[u]] 個子串,這些子串擁有相同的貢獻,他們的貢獻都是 cnt[u] 。

字典序第k大的子串:

由於「子串對應從初始狀態開始的路徑」,那麼實際上要找字典序第k大的路徑。用動態規劃求出從每個狀態開始有多少種路徑。

假如是求本質不同的,則使用

dp[u]表示從狀態u開始的路徑的數量。

\(dp[u]=1+\sum dp[v]\)

假如不是求本質不同的,則每一種節點開始並結束有cnt[u]種,也就是

\(dp[u]=cnt[u]+(\sum dp[v])\)

bool vis[2 * maxn];

ll dp[2 * maxn];

ll dp(int u)

vis[u] = 1;

return dp[u];

}void show(int u)

// printf("u=%d\n", u);

// printf(" lnk=%d\n", lnk[u]);

// printf(" cnt=%d\n", cnt[u]);

// printf(" dp=%lld\n", dp[u]);

// for(auto v : res)

// printf(" v=%c %d\n", v.first, v.second);

for(auto v : res)

show(v.second);

return;

}void calc2(int k) }}

}putchar('\n');

}

字串 字尾自動機SAM

includeusing namespace std typedef long long ll const int maxn 1e6 50 struct nodenode maxn 1 int las 1,tot 1 結點node表示乙個類 las是未加入此字元前的最長字首 整個串 所屬的結點編號 ...

字串 最小字尾自動機與有限狀態自動機

1 最小字尾自動機 乙個串x的最小字尾自動機sa suffixautomaton 記為sa x 即識別串x的所有字尾的最小確定自動機.如圖1所示,該自動機可以接受串baabbaa的以下所有字尾 a,aa,baa,bbaa,abbaa,aabbaa,baabbaa.已經存在以線性複雜度構造sa的演算法...

字串模板(字尾陣列 字尾自動機 回文樹)

再整理一遍板子 例題cf432d 問對字串s,對於所有的字首,當它等於同長度字尾時,這個子串一共在s 現多少次。字尾陣列求lcp是logn,顯然直接二分即可。複雜度nlogn 123 4567 891011 1213 1415 1617 1819 2021 2223 2425 2627 2829 3...