關於SAM和廣義SAM

關於SAM和廣義SAM

不是教程

某些思考先記下來

SAM

終於學會了這個東西誒......

一部分重要性質

確定一個重要事情,S構造出的SAM的一個重要性質是當且僅當對於S的任意一個後綴,可以從1號節點走到終止狀態。專業的名詞叫做有限狀態自動機。

trans[st][c]表示的是對於狀態st,如果將st中任意串s加一個c,那麼會到達的新狀態new,顯然new是唯一的。假如不唯一那麼s一定不屬於同一個st。

fa[st]表示的是對於狀態st,如果慢慢縮小st中後綴長度,會到達的第一個狀態。規定一個虛點\(1\)全集,那麼fa能夠構成一個樹形結構(parent tree)。

很顯然\(endpos[st]\subset endpos[fa[st]]\)。所以雖然\(\sum|endpos|=O(n^2)\)但是可以通過線段樹合併得到endpos集合。你也可以發現Parent Tree任何一個到根的鏈是一個長度遞減且連續的某個後綴的前綴。

trans構成的有向圖G一定是一個DAG,因爲可以建立這個圖從1到任意點的路徑構成的串對應的原串,所以有環就出大事了。

G上任意一條從1開始走的路徑都代表一個後綴的前綴——也就是一個子段。

而對於G任意一個i節點開始走的路徑,表示的是對於後綴i的任意一個子段。

可以發現一個確定的Trans[][]可以唯一確定一個串和SAM的形態。因爲DAG的最長路是原串。

構建和複雜度證明

先說構建,構建的目的是要讓這個東西能夠識別新的後綴並且還要維持好Parent Tree的各種性質。

設原來是S[1...r]現在我們要變成S[1...r+1],S[r+1]=c:

首先可以發現的事情是,新狀態一定包括\(S[1....r+1]\),設這個新狀態爲u,那麼首先我們要讓所有\(S[i...r+1]\)都被表示出來,我們可以一直跳\(v=r\)的fa,根據定義如果沒有trans[v][c]我們要更新trans[v][c]。然後一直跳如果直到1才停下來,說明之前\(S[(i \in[1,r])...r]\)都沒出現過,那沒事了。

但是如果到中間一個\(v\)而trans[v][c]=x,說明\(v\)代表的\(S[i...r+1]\)之前被表示出來過,但按照parent tree的定義我們要更新endspos。此時有一個問題:

x代表的串最長的那個串可能不是我們當前要使得自動機以後識別的串S[i...r+1]。根據parent tree那個一條鏈的性質,這個集合一定要假如x的所有父親的endpos集合中,但是可以發現,比S[i...r+1]小的串構成的endpos集合和大於等於S[i...r+1]的串endpos集合現在多了一個r+1,所以我們把x分裂成上下兩半y和x',然後讓x'和u共同當y的兒子。此時有一個問題是我們要更新一下trans,也就是把之前DAG圖上到x的邊全部轉到y上,根據trans還有fa的性質這些邊一定是v的連續一段fa,我們暴力修改一下(複雜度等下分析)。更新後的DAG只能只能識別原來的串+c這些串了(trans全更新了),並且我們成功更新了endpos。


分析一下複雜度:

首先如果trans[v][c]=NULL我們修改成r+1的這一部分複雜度是沒問題的。因爲你修改一次意味着一條DAG上的邊,而DAG上的邊是\(O(n)\)的。

唯一的問題是到了v之後,你還要跳連續的v的trans[v'][c]=x的一段鏈。這裏複雜度是多少?

根據trans和Parent Tree分析一下,可以發現,任何一段暴力跳都可以對應上一個另一條樹鏈上的一段,且另一段大小一定不小於這一段。那麼一直這樣對應下去一定可以找到一個最長的鏈。由於parent tree總點數是\(O(n)\)的,所以顯然暴力跳的次數遠遠小於\(O(n)\)

廣義SAM

普通SAM是針對一個串而言的,而廣義SAM是針對一個trie樹而言的。此時後綴和endpos的定義一一擴大,後綴變成了包括葉子的一段綴,endpos變成了trie樹上的節點編號。

考慮一下對於一個串我們弄SAM出來利用的性質,我們只是利用了後綴的後綴也是後綴以及一個節點加上一個字符c後是不是仍然是後綴是確定的,在trie樹上,葉子的綴的綴也是葉子的綴是顯然的。現在SAM表達的含義是trie樹上的節點,trans[u][c]表示的是在u這個點加上一個c字符後會到trie樹上哪個點。

考慮以後我們之前的增量構造的含義,意義其實就是更新了到根的一條鏈的後綴,在這些後綴的後面加上一個字符。

一個構造方法是按照trie樹bfs序構造。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章