trie
複習感想——數據結構篇其四_trie及其變形//從一次失敗的嘗試談起
Trie樹是用來處理多個字符串的數據結構。
Trie樹的初始動機很簡單,利用字符串間的公共前綴來節約字符串存儲的空間及加快檢索的速度。
例如:單詞 computer和command在trie樹中只需要存儲 com 和 puter及mand三個部分
Trie樹中從根節點出發到樹上任意節點的路徑都可以代表一個單詞。
Trie樹很直觀,其插入刪除查找操作也很簡單,這裏不再贅述。
下面着重看一個問題,我曾經對這個問題做過一個很失敗的嘗試,但是通過對trie的改進版能得到這個問題的很漂亮的解。
例一:病毒
二進制病毒審查委員會最近發現瞭如下規律:某些確定的二進制串是病毒的代碼。如果某段代碼中不存在任何一段病毒代碼,那麼我們就稱這段代碼是安全的。現在委員會已經找出了所有的病毒代碼段,試問,是否存在一個無限長的安全的二進制代碼。
例如:如果{011,11,000000}是病毒代碼集,那麼0101010101。。。就是一個無限長安全代碼。如果{01,11,000000}是病毒代碼集,則不存在無限長安全代碼。
分析:對於這個問題,我最開始的想法很簡單(事實證明也很天真)
1. 設L是病毒集中最長的代碼的長度。那麼,容易證明,如果存在2L-1的長度的安全代碼,那麼,一定存在無限長安全代碼。
2. 對2L-1的長度的所有代碼都進行一次kmp。。。
我相信,這是一個很正常的“最初想法”,不難發現這個方法很慢,指數級的複雜度。。。
用類似於trie樹的遞歸實現可以稍微快一點,但是依舊是指數級的複雜度。ToT
分析一下這個方法爲什麼這麼慢。
1. 對於任意的病毒字符串來說(設其長度爲l),那麼對於這個字符串來說,要找出它的匹配,我們所關心的字符長度其實是 l 而不是2L-1。
2. 做了大量的無用功,以病毒字符集{011,01,000000}爲例。
在程序過程中,如果發現011代碼串是不安全的,那麼,以這個爲依據,我們可以直接論斷0011,1011,00011。。。。。等等代碼串都是不安全的,但是在那個失敗的嘗試中,對於這些代碼串都進行了匹配。
這兩個問題有種似曾相識的感覺。。。。。。確實曾經見過類似的動機(特別是第二個),同爲字符串匹配算法的KMP算法的初始動機就很類似。KMP在解決一維字符串匹配問題的時候利用了自動機的思想,在這裏也能用到!(而且這裏用到的自動機我覺得在直觀上更像自動機,畢竟我第一次接觸自動機是在”數字邏輯電路”這門課上,當時的名字貌似叫狀態機什麼的。。。。)
接下來以病毒代碼集爲{011,11,000000}的情況爲例說一下用trie的變形的具體作法,以及分析一下其中每一步的動機:
1. 把病毒代碼集建成一顆trie樹。動機:每一個具體的病毒所關心的代碼長度爲它本身的長度,如圖:
其中黑色節點所代表的是病毒代碼。
2. 在此圖中添加一些邊:完整地作出添加邊後的圖很麻煩,下圖中僅僅是考慮了01字符串後添加的邊。
什麼叫考慮 01字符串後添加的邊呢??
任何一個 以01開頭的字符串a,在判定a中是否存在病毒代碼的目的下,等價於判斷把a中的第一個字符:0去掉以後得到的字符串b 中是否有病毒代碼。
同樣的道理,以001開頭的字符串等價於該字符串去掉前綴 00後的字符串
以101開頭的字符串等價於以該字符串去掉前綴 1後得到的字符串。
3. 如果理解了這個建圖的道理,那麼,很容易發現,如果存在一個無限長字符串 a,它能一直在圖中的白顏色節點中“走”,那麼,對於該病毒集存在一個無限長完全代碼,從而可以把這個問題轉換爲判斷是否在白色節點中存在迴路的問題(因爲白色節點個數是有限的),那麼僅需要對白色的節點進行一次dfs即可,成功地把複雜度降到了線性
4. 至此,此問題已經圓滿解決。