字尾自動機 筆記

2021-08-10 12:03:53 字數 3566 閱讀 3029

參考了hihocoder和clj的課件,看了看hzwer的**,懂了些東西,記一下。

字尾自動機是一棵trie樹

給出乙個字串s,對於s的乙個子串s,right(s) 代表乙個集合,為s在s中所有出現的結束位置集合。

以s=」aabbabd」為例,right(「ab」) = ,因為」ab」一共出現了2次,結束位置分別是3和6。同理right(「a」) = , right(「abba」) = 。

對於乙個字串集合,若這個集合中所有的字串擁有相同的right集合,那麼我們可以給這個集合乙個狀態s, 用substring(s)代表這個集合,這個集合也對應著乙個right(s)集合。

那麼現在就存在乙個s substring right集合的一一對應關係。

對於substring(s),存在longest(s)和shortest(s)分別代表substring(s)中的最長和最短字串,其長度分別為l(s)與r(s)。

對於乙個longest(s),我們不斷地去除他的第乙個字元,直到其變為shortest(s),這個過程中出現的l(s)- r(s) + 1個字串必然都屬於substring(s)。

若我們將shortest(s)的第乙個字元刪去(此時當然是假定shortest(s)不為空串),必然會導致狀態的改變,因為此時的right集合變大了,而s和right(s),substring(s)一一對應。

具體可以參照下圖。

我們用fa(s)來代表從s進行的一次跳轉到達的新狀態。

若把狀態按fa來建圖,顯然可以得到乙個樹形結構,我們稱之為parent樹。

對於這棵樹存在以下性質:

1. max

(fa(

s))=

min(

s)−1

m ax

(fa(

s))=

min(

s)−1

。 2. rig

ht(s

)∈ri

ght(

fa(s

))r ig

ht(s

)∈ri

ght(

fa(s

))。定義st(t)為從空串狀態走了字串t到達的狀態,trans[p][c]代表從p狀態新增新的字元c能夠到達的狀態。

若干個狀態作為點,構成了字尾自動機,這個字尾自動機藉由trans組成了trie樹,而這個trie樹上的部分節點又類似ac自動機的fail那樣構成了parent樹。

構造方法clj課件已經給出了,這裡掛上**並標以注釋。

namespace sam

void init()

void add(int c) }}

void build(char *str)

};

需要注意的是**中的l代表的是longest,shortest可以根據父親節點的longest推出來。

…………………………………………………………………………………………………………

昨天做男人8題,sam簽到,惡補之後過了,於是回來填坑。- 2018.3.26

substrings spoj - nsubstr

對於乙個子串,它所對應的right集合的大小即為當前子串出現的次數,由於right集合的包含關係,推導出所有節點的right的大小。

那麼我們如何求解right集合呢?

~1. 首先sam上得到的圖是dag,需要拓撲排序使得其有序化。

void topo() 

queue

q;q.push(1);

while(!q.empty()) }}

~2. 把初始串能夠得到的位置的right集合大小設為1。

int p = 1;

for(int i = 0; i

< len; i++)

~3. 在拓撲排序有序後的序列上,倒著把子節點的right並到父節點上。

for(int i = g.size()-1; i >= 0; i--)
這樣我們就能求出所有的right集合的大小。

回到這個問題,我們想要知道的是長度為x的子串出現的最大次數。

首先對於每個節點,我們通過計算出這個節點對應的right集合,可以知道這個節點對應的長度為shortest至longest的子串出現次數都為right,其實我們只需要在意長為longest的子串出現的次數即可,因為長度小於它的出現的次數必然只會大於等於它。所以我們記錄下每個節點對應的長為longest對應的答案,然後倒著dp即可。

for(int i = 1; i

<= cnt; i++)

dp[l[i]] = max(dp[l[i]], right[i]);

for(int i = len; i >= 1; i--)

dp[i] = max(dp[i], dp[i+1]);

a string game直接建sam,然後在dag上跑sg函式即可。

#include 

#include

#include

#include

#include

#include

#define n 440000

using

namespace

std;

typedef

set::iterator itr;

int n,k,cnt;

string a[n];

int g[n],sum[n];

struct edgee[n<<1];

struct sam_

inline

void add(int x,int id)

}p=q;

}void init()

} sam;

int sg[n];

//int dfs(int p) ;

for (int j = 0; j < 26; ++j)

for (int j = 0; j < 27; ++j)

}return sg[p];

}char s[n];

int gao()

return sg[p];

}int main()

memset(sg, -1, sizeof sg);

dfs(1);

int m, ans = 0;

scanf("%d", &m);

while (m --) ans ^= gao();

if (ans) else }}

字尾自動機

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