[轉]尾碼自動機

來源:互聯網
上載者:User

標籤:

原文地址:http://blog.sina.com.cn/s/blog_8fcd775901019mi4.html 

感覺自己看這個終於覺得能看懂了!也能感受到尾碼自動機究竟是一種怎樣進行的資料結構了...

筆者自己的話會用楷體表示出來...[說不定能協助大家理解,但是可能也破壞了大家的自主理解力?所以...看不懂的話再來看好咯...]

 

常用的字串處理工具:

1.       整詞索引:排序+二分;Hash表。可以解決整詞匹配,但不支援首碼搜尋;Hash表在模式串定長的情況下可以用RK解決多模式串搜尋和匹配問題。總的說來整詞索引在子串搜尋裡面的效能並理想。當然也有優點,就是空間小。

2.       首碼索引:KMP/Trie樹/AC自動機。AC自動機可以看成KMP + Trie樹的混合體,因為KMP支援單串搜尋和fail指標;而Trie樹支援多串搜尋,但沒有fail指標。兩個雜交在一起就有了AC自動機,既支援多串匹配也有fail指標,O(n)的時間之內可以掃描出所有的模式串,確實很強大。

3.       尾碼索引:尾碼樹、尾碼數組。尾碼索引一般只針對單串進行處理,可以反應該單串的內部結構資訊(字典序、最長公用首碼等),當然也可以把多個串合并在一起做尾碼索引,進而找到這些串之間的結構資訊。

 

應該說單串索引裡面,尾碼數組已經非常強大了,已經可以解決很多問題。而尾碼自動機我以前都沒怎麼聽說過,網上查了下,好像沒有太多資料介紹它,有啥用也沒說。貌似很多它能完成的工作,尾碼數組也能完成;它不能完成的,尾碼數組也能完成。(額,被完爆了)。

儘管如此尾碼自動機還是有很迷人的一面:代碼量還不到50行,太短了,太誘人了;而且是O(n)的線上演算法,O(1)(均攤)增量式構造。相比之下,尾碼數組的最大短板在於不能增量構造,雖然用一些離線的演算法可以解決這個問題,但這種trick只在競賽裡面有些用,實際工程裡面,該線上的演算法還是必須線上。作為一個弱菜,我個人對演算法的審美一直都是“簡單而有用的東西,就是美的!”,尾碼自動機算是一個吧,所以花了一點時間學習。本文主要是個人的總結,詳細參考clj的ppt。

 [下面這段關於資料結構的話筆者十分贊同]

我們知道資料結構是一個二元組 = 資料 + 操作。資料結構的靈魂在於,在對資料進行操作的過程當中,保持某些性質不變從而高效的完成任務。這些性質往往分為兩種:1. 功能性質; 2. 效能性質。 以平衡二叉樹為例:功能性質是左右子樹有序,進而實現按key檢索、添加、刪除;而效能性質是保持左右子樹的深度平衡,以實現O(log(n))的操作上限。變化的操作當中保持關鍵性質不變,個人認為是資料結構設計的核心內容。

 

再來看尾碼自動機,後面的內容主要是個人總結。

尾碼自動機的功能性質是什嗎?

尾碼自動機只對尾碼感興趣。對於字串str,設SAM(str)是其對應的尾碼自動機,則SAM(str)接收且僅接收str的所有尾碼。也就是說對於str的所有尾碼,在SAM(str)裡面都有合法的轉移,且轉移到終態。作為附加功能,使得尾碼自動機不僅僅能識別尾碼,也能識別str的所有子串。[區別就是一個要轉移到終態,一個會在中間停下]

尾碼自動機的效能性質是什嗎?

str的長度為n,則尾碼自動機SAM(str)的狀態數為O(n)。因為只有n個尾碼,所以狀態數為O(n)好像也挺合理,一一對應嘛。但是別忘了,尾碼自動機不僅僅能識別尾碼,也能識別str的所有子串,這些子串的個數是O(n^2)的,這一下就變得不合理了,O(n)的狀態數的如何做到識別O(n^2)個字串的?在首碼索引裡面,以trie樹為例,狀態和首碼(字串)是一一對應的,所以trie樹的狀態數上線剛好等於串的總長度(也是首碼數),可以認為是O(n)的。但是尾碼自動機卻不能這樣幹,也來一一對應,這樣乾的後果就是狀態數也變成了O(n^2)。這意味著,要實現O(n)的狀態數,就必須使得一些子串被映射到同一個狀態,以實現狀態的重複利用!這裡的問題又產生了?該把哪些串映射到同一個狀態?隨便搞行嗎?

 

關鍵概念 + 主要觀察:

1.       Right集: 對於str的任何一個子串s,Right(s)為一個集合,該集合包含s在str裡面所有出現區間的終點。比如子串s在str中出現了k次,有s = str[l1, r1) = str[l2, r2) = ……. = str[lk, rk),則Right(s) = {r1, r2, ……, rk}。

2.       狀態及其意義: 字串s1, s2被映射到相同的狀態,若且唯若他們有相同的Right集。即State(s1) = State(s2)若且唯若Right(s1) = Right(s2)。(也就是說狀態可以被重複利用的)。

3         狀態的性質1——Right集: 由於映射到相同狀態的串具有相同的Right集,那麼Right集不僅可以作為字串的性質,也可以作為狀態的性質。對於SAM(str)的任何一個狀態x,用Right(x)表示對應的Right集。Right集其實也可以看成尾碼的集合[就是以Right(x)中的某個元素r打頭的尾碼Suffix(r)],這些尾碼可以被x的後繼狀態接收;反過來,狀態x到終態的任意一條路徑也對應str當中的某個尾碼,這個尾碼應該屬於Right(x)。從這個層面上來講,位置r屬於Right(x)的充要條件是,Suffix(r)能被x的後繼狀態識別。

4.       狀態的性質2——字串:令SubStr(x)表示所有轉移到狀態x的子串。

5.       Right(x)與SubStr(x)之間的關係:

  SubStr(x) -> Right(x),任意給定SubStr(x)當中的一個字串s,根據定義就可以確定Right(x)這個很直接[因為這個狀態的Right就是任意一個SubStr(x)中串的Right];

  Right(x)->SubStr(x),沒有上面的那麼直接,如果任意給定Right(x)當中的一個位置r,我們該如何確定SubStr(x)?答案是字串長度!如果知道長度len,很容易知道str[r – len, r)是屬於SubStr(x)的;如果知道所有的長度,就能確定整個SubStr(x)!

  可以證明:SubStr(x)當中字串的長度剛好構成了一個連續的區間,可以用[max(s), min(s)]表示。

  [怎麼證明呢?]

    先舉個例子如字串"abcd"。

    那麼"abcd","bcd","cd","d"的Right集合是相等的,它們應當被投射的一個狀態下。

    於是就發現這些Right集合相等的元素之間其實有著很深的聯絡...它們之間是內含項目關聯性!而且是尾碼的內含項目關聯性!

    然後一般化的思考,如果已經知道一個狀態下的最長串,可以很清楚的知道,這個串的所有尾碼的Right集合一定包含這個串的Right集合[很顯然,可以腦中跟著出現一個線段進行思考]。

    當然到了某個位置之後的尾碼,可能就會多出那麼幾個位置,如串"abcdcd","abcd"和"bcd"的Right集合是相等的,但是"cd"和"d"卻多了一個地方,狀態有所不同。至於為什麼是連續的,也十分顯然,因為不可能斷開。

    而仔細思考Right(x)->SubStr(x)這條性質會發現其實Right集合等於這個的,也僅僅只有這個最長串和它的某些尾碼!因為位置固定了,只有這一些。[感覺很囉嗦,不過筆者到了後面才理解這句話,其實在這就可以看出]

6.       狀態與狀態之間的關係: 對於兩個不同的狀態x, y。要麼Right(x)與Right(y)是空集,要麼一個是另一個的真子集。(這條性質保證了狀態總數是線性)。

  [怎麼證明呢?]

  假設Right(x)={a1...ak1},Right(y)={b1...bk2}。

  若有ai=bj=r,則可以在substr(x)中取一個串A,在substr(y)中取一個串B,因為這兩個串在r處末端重合,則要麼A是B的尾碼,要麼B是A的尾碼[A!=B],那就不妨設A是B的尾碼。

  那麼在B出現位置的末端A一定也出現了,也就是A所在的Right集合一定包含了B所在的Right集合,又因為x!=y,所以x包含y。

  所以如果Right集合有交集,那麼就一定相互包含。

7.       Parent樹: 根據性質6,對於每一個狀態x,我們可以如下確定一個Parent(x)。 y = Parent(x)若且唯若,Right(y)是包含Right(x)的所有集合當中最小的那個;如果這樣的y不存在,則置Parent(x)=初始態。要注意的是,這個Parent並不是轉移關係裡面的前驅,尾碼自動機裡面一個狀態有多個前驅,卻只有唯一一個Parent。另外,從Right集的角度來看Parent樹,從葉子往根走,其實就是一些相交集合不斷合并的過程。因此Parent指標的一個指向某個狀態的本質意義:就是對Right集進行擴充,將一個Right集加入另一個Right集!同時,因為有了Parent樹,我們不必對每個狀態都存一個Right集,相反用一種層次化的結構來存,保證了空間複雜度是線性。

8.       Parent(s)與s之間的關係:max(Parent(s)) = min(s) – 1;trans(s, ch) != null則trans(Parent(s), ch) != null。

  [怎麼解釋呢?]

    Parent(s)是包含s的最小集合,也就是斷開位置所在的Right集合

    例如"abcdcdd"中Right有三個{4}{4,6}{4,6,7},其中"abcd","bcd"屬於{4},"cd"屬於{4,6},"d"屬於{4,6,7},這些都是逐個移動中發現的第一個斷開的位置。所以max(Parent(s))=min(s)-1.

    trans(s,ch)應該說的是s引出的轉移邊ch,那我們就考慮到從它們公用的位置引出的尾碼,因為Parent(s)的Right更大,所以s所能引出的,Parent(s)也能引出,所以如果s能轉移,那麼Parent(s)也能轉移。

9.       轉移字元、前驅:如果有trans(x1, c1) = trans(x2, c2)……=trans(xk, ck) = x,則c1 = c2 …… = ck!且狀態x1, x2, ……xk在parent樹中構成一段連續的Parent鏈(即有父子關係)。鏈的最底部最小的兒子為xi,若且唯若step[xi] + 1 = step[x](step為構造新節點是給與的標記)!

[怎麼證明呢?]

 

還有關於 step[xi] + 1 = step[x]的證明,暫時不知怎麼證明...

 

再來理解尾碼自動機的構造演算法:

SAM(T)到SAM(Tx)需要更新什嗎?我們的依據是什嗎?

首先來看我們需要保持哪些性質?

a)       轉移合法性:接收Tx的所有尾碼,且保證Tx的所有子串有合法轉移!(因此涉及轉移矩陣的更新!)

b)       狀態合法性:每個狀態和新增狀態的right集滿足定義,即:轉移到同一個狀態的所有子串有相同的Right集。(涉及Parent鏈的更新)

對於a),因為Tx的子串 = T的所有子串+ Tx的所有尾碼[就是包含x的和不包含x的],因此我們只需要保證Tx的所有尾碼有合法轉移就行!而Tx的尾碼完全是由T的所有尾碼增加一個字元x得來的,我們只需要挨個找出T的尾碼在SAM(T)中的狀態,然後再保證這些狀態在SAM(Tx)當中有x轉移即可!

如何找出SAM(T)中尾碼對應的狀態?由於T的所有尾碼有一個公用的出現位置r = length(T),這導致他們的狀態Right集交集非空,進而根據性質6知道,這些狀態肯定是構成一個Parent鏈,所以要找這些狀態最簡單的辦法就是沿著Parent鏈回溯![下面的內容:畫圖大法好!]

設SAM(T)當中尾碼對應的終態={v1, v2, …….vk},回溯的時候會出現哪些情況呢?

一個是trans(p, x) = null,即不存在x轉移,我們就必須增加一個x轉移SAM(Tx)的終點np即可,同時保證了性質b);

那q = trans(p, x) != null的時候呢?很好嘛,已經有x轉移了,我們只需要保持性質b)就行。擴充Right(q),即把np的parent的指標鏈向q不就ok了嘛!額。。但這是有問題的!

我們先來看轉移到q的前驅有哪些,根據性質9不妨設q有m個前驅:p1, p2, …….,pm,且滿足(parent[p1] = p2, parent[p2] = p3, ….)。

現在的問題是,p在這條鏈的哪個位置?

如果p = p1(此時step[p] + 1= step[q] ),前面的做法是沒有問題的,因為從p2……pm轉移到q的所有字串,必定是從p轉移到q的字串的尾碼,所以這些字串也必定出現在位置length(Tx),因此擴充Right(q) = Right(q) + {length(Tx)}當然沒問題!但是,如果p != p1就麻煩了,對於出現在p前面任何一個節點pj,可以證明從pj轉移到q的字串一定不會出現在位置length(Tx)。 如果還是簡單的令Right(q) = Right(q) + {length(Tx)},就導致性質b)對節點q失效,因為有些串轉移到q但是它的Right對不上! 那如何辦呢? 可以看到這裡p把前驅分成了兩部分,前面一部分轉移到q的right集不變;後面一部分的right集應該要擴充。最簡單的辦法就是把q拆也成兩個,對應兩部分前驅,這就是構造演算法裡面的做法!其實理解這個做法的關鍵點就是性質9 !

[轉]尾碼自動機

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.