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...