知識點 字尾自動機

2022-05-19 02:55:09 字數 2819 閱讀 2420

字尾自動機:

簡介:定義:

1.字尾自動機的結構類似於ac自動機,每個點表示乙個endpos等價類(子串結束位置的集合,以下簡稱為狀態),邊同ac自動機中的邊。即字尾自動機上從根到乙個點有若干條路徑,這些路徑構成的子串在原串中的endpos集合都相同。

2.parent樹是一棵建立在字尾自動機節點上的樹,建立的方法是類似於線段樹的分割(見下文)。它滿足任意點u代表的任意乙個字串是u子樹中任意乙個字串的字尾。

3.一些需要維護的資訊:

struct

node;

性質:在提到性質之前我們先考慮一下這個endpos等價類的含義。

以$aababa$這個串為例,它有6個可能的endpos,我們現在按長度依次考慮以i為endpos的每個子串$(s[1,i],s[2,i],\cdots ,s[i,i])$所屬的endpos等價類。

初始都是空串,那麼$endpos(空串)=\$。

然後考慮長度為1的子串,位置1,2,4,6是'a',位置3,5是'b'。那麼$endpos('a')=\$,$endpos('b')=\$。

然後考慮長度為2的子串,位置2是'aa',位置3,5是'ab',位置4,6是'ba'。那麼$endpos('aa')=\$,$endpos('ab')=\$,$endpos('ba')=\$。

自己模擬一下這個過程,不難發現三點性質:

1.隨著長度的增加,endpos等價類從集合$\$逐漸劃分成若干個小集合。但不是完全劃分,中間會因為某個字首的長度不夠而損失掉這個字首。

2.對於任意兩個子串,它們的endpos等價類要麼互為子集,要麼不相交。顯然前者是互為字尾的情況,後者是不互為字尾的情況。

3.同乙個endpos等價類包含的子串實際上是某個極長子串的所有字尾。因為我們是在逐漸往每個endpos前面塞字元,那麼某個等價類裡的所有endpos前面塞完乙個字元後,它們代表的字串可能從相同變得不同。此時該等價類被劃分,它的極長子串就是塞之前的子串。

構造:使用增量構造法,就是假設前n-1個字元已經構造好了,考慮插入第n個字元c會怎麼樣,也就是如何處理插入後新產生的子串(新串的n個字尾)。

先建乙個代表$\$等價類的點np,然後從上一次插入的點last(即代表$\$等價類,$s[1,n-1]$的點)開始跳fa,每跳到乙個點p就把它往np連一條字元邊c。直到跳到0(不是根)或者當前點p已經有一條字元c出邊。

如果跳到0了說明原串裡根本就沒出現c這個字元,於是直接記$x[np].fa=rt$即可。

否則,設$q=x[p].to[c]$,分q是或不是新串的字尾兩種情況討論。

當且僅當$x[p].dis+1=x[q].dis$時,q是新串的字尾(q裡的最長串就是p裡的最長串+c)。此時新串長度$>x[p].dis+1$的字尾都連過邊c了,而長度$\leq x[p].dis+1$的字尾已經在原串出現過了(就是q代表的所有子串),所以整個系統合法,只需要令$x[np].fa=q$。

否則,q不是新串的字尾(q裡的最長串的字尾是p裡的最長串+c)。此時我們把q拆成兩個點nq和q,前者是新串的字尾(即長度$\leq x[p].dis+1$的部分),後者不是。拆完之後把p所有的祖先原來連到q的c邊都改到nq(因為p的祖先+c是新串的字尾),然後記$x[nq].fa=x[q].fa,x[q].fa=x[np].fa=nq$即可。此時整個系統合法。

關於為什麼$x[p].dis+1\neq x[q].dis$時q不是新串的字尾:因為如果是,那麼q中最長串去掉c後必然是乙個長度$>x[p].dis$的原串字尾,且它代表的節點有c出邊。那麼這個節點應該先於p被遍歷到並且停下,而這並沒有發生,所以q一定不是新串的字尾。

**:

#include#define maxn 1000005

#define maxm 500005

#define inf 0x7fffffff

#define ll long long

#define debug(x) cerr<

namespace

std;

int hd[maxn<<1],nxt[maxn<<1],to[maxn<<1

],ans,cnt;

char str[maxn<<1

];inline

intread()

inline

void addedge(int u,int v)

struct

samx[maxn<<1

];

int tot=1,lst=1,rt=1

; inline

void extend(int

pos)}}

inline

void dfs(int

u)}at;

intmain()

字尾自動機

廣義字尾自動機:

沒啥區別,每次插入乙個串的時候直接把last設為1就行了,相當於從頭開始插入。

#include#define maxn 1000005

#define maxm 500005

#define inf 0x7fffffff

#define ll long long

#define rint register int

#define debug(x) cerr<

namespace

std;

char

str[maxn];

inline

intread()

struct

ramauto}}

}inline ll dfs(

intu)

}ram;

intmain()

廣義字尾自動機

字尾自動機

基礎知識 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 罪...