首先,字尾自動機的性質:從根節點開始可以識別乙個字串的所有子串
接下來,我們需要給出乙個定義:
將字尾自動機上的pre指標反指,會得到一棵樹形結構,我們把這棵樹叫做parent樹!
parent樹是原串反串的字尾樹!
記住這個定義,因為字尾自動機中大部分題都與這棵樹有千絲萬縷的聯絡。
我們畫出前文字尾自動機的parent樹(即串abbab)
(注意:這裡邊上畫出的a,b主要用於區分這兩個節點,暫不討論實際意義)
用綠色標記的是**節點。
那麼可以發現,在parent樹上父節點一定是子節點的乙個字尾
那麼我們討論乙個經典問題:
乙個詢問字串在原模板串**現了多少次呢?
這個問題有很多種解法,包括hash,kmp等等,時間複雜度比較優越。
這裡介紹一種字尾自動機的方法,時間複雜度o(卡死)
我們從parent樹出發
我們知道,乙個字串的所有子串是它字首的字尾
而我們是可以在建造字尾自動機的時候標記字首節點的!(事實上,除了**出來的節點,其他節點都屬於字首節點,因為都能在事實上對應出原串的乙個字首)
同時,如果詢問串是模板串的乙個子串,那麼這個詢問串一定是這個模板串乙個字首的字尾!
所以我們先在字尾自動機上識別詢問串,如果無法識別直接答案為0
如果成功識別,那麼我們只需要求出這個串是多少個字首的字尾就可以了
我們知道,parent樹上的乙個父節點是他所有子節點的字尾
所以,我們只需要統計出以這個節點為根的子樹內字首節點的個數就可以了!
而字首節點只需在字尾自動機上打endpos標記就可以了!
這樣這個問題就圓滿地解決了
雖然實際上,這個問題沒有任何應用字尾自動機的價值,而且用字尾自動機反而會導致時空超限,但我們還是以他為起點,因為後面的大部分應用都要基於這種方法
接下來看看模板題:
例:洛谷3804
求乙個字串中子串長度與出現次數乘積最大值,要求出現次數不為1
有了上面的經驗,我們已經知道如何統計乙個子串出現次數了
而乙個子串的長度,不就是len變數嗎?
那就完事了呀!在parent樹上跑一遍dfs即可!
**如下:
#include #include #include #include #include #include #include #include #define ll long long
using namespace std;
struct sam
s[5000005];
struct edge
edge[2000005];
int head[5000005];
char ss[1000005];
int cnt=1;
ll ans=0;
int siz;
int las;
void init()
void add(int l,int r)
/*void res()
*/void ins(int c)
else
else
}} las=nwp;
}void buildtree()
}void dfs(int x)
if(s[x].endpos>1)ans=max(ans,1ll*s[x].len*s[x].endpos);
}int main()
buildtree();
dfs(1);
printf("%lld\n",ans);
return 0;
}
字尾自動機
基礎知識 step i 表示的是字串i在原字串中的位置。pareint i 表示root到parent i 的子串是root到i的最長字尾。字尾自動機遍歷可以得到原字串的所有子串。特殊技巧 一 字尾自動機的不同子串數有兩種求法 1.ans step i step parent i 1 i cnt 2...
字尾自動機
常用於處理字串問題,可以高效解決許多字串問題。有點像將乙個字串的所有字尾都建在乙個ac自動機上,但不同的是字尾自動機的節點數最多為2 n,因為它只記錄需要記錄的點,一些沒有記錄東西的點可以視為與下面有價值的節點並在一起,這樣大大降低了時間複雜度和空間複雜度。對於每乙個節點記錄它的後面加上每個字元後字...
字尾自動機
基礎學習 簡潔明瞭的講解 總狀態數不超過2n 12n 1 2n 1 包括初始狀態 統計每個end po sendpos endpos 等價類出現位置數量時,要按長度從長到短的計算cnt cntcn t。那為什麼一定要從長到短呢?比如回文自動機就直接是按照節點編號從大到小計算cnt cntcn t 罪...