天天看點

經典算法題每日演練——第七題 KMP算法

      在大學的時候,應該在資料結構裡面都看過kmp算法吧,不知道有多少老師對該算法是一筆帶過的,至少我們以前是的,

确實kmp算法還是有點饒人的,如果說紅黑樹是變态級的,那麼kmp算法比紅黑樹還要變态,很抱歉,每次打kmp的時候,輸

入法總是提示“看毛片”三個字,嘿嘿,就叫“看毛片算法”吧。

一:bf算法

     如果讓你寫字元串的模式比對,你可能會很快的寫出樸素的bf算法,至少問題是解決了,我想大家很清楚的知道它的時間複

雜度為o(mn),原因很簡單,主串和模式串失配的時候,我們總是将模式串的第一位與主串的下一個字元進行比較,是以複雜

度高在主串每次失配的時候都要回溯,圖我就省略了。

二:kmp算法

   剛才我們也說了,主串每次都要回溯,進而提高了時間複雜度,那麼能不能在“主串”和“模式串”失配的情況下,主串不回溯呢?

而是讓”模式串“向右滑動一定的距離,對上号後繼續進行下一輪的比對,進而做到時間複雜度為o(m+n)呢?是以kmp算法就是

用來處理這件事情的,下面我們看下簡單的例子。

經典算法題每日演練——第七題 KMP算法

通過這張圖,我們來讨論下它的一般推理,假設主串為s,模式串為p,在si != pj的時候,我們可以看到滿足如下關系式

si-jsi-j+1...sn-1=p0p1..pj-1。那麼模式p應該向右滑動多少距離?也就是主串中的第i個字元應與模式串中的哪一個字元進行比較?

假設應該與模式串中的第k的位置相比較,假如模式串中存在最大的字首真子串和字尾真子串,那麼有p0p1..pk-1=pj-kpj-k+1...pj-1.

這句話的意思也就是說,在模式p中,前k個字元與j個字元之前的k個字元相同,比如說:“abad”的最大字首真子串為“aba",最大

字尾真子串為“bad”,當然這裡是不相等,這裡的0<k<j,我們希望k接近于j,那麼我們滑動的距離将會最小,好吧,現在我們用

next[j]來記錄失配時模式串應該用哪一個字元于si進行比較。

設 next[j]=k。根據公式我們有

                -1        當j=0時

next[j] =   max{k| 0<k<j 且 p0p1..pk-1=pj-kpj-k+1...pj-1}

                0         其他情況

好,接下來的問題就是如何求出next[j],這個也就是kmp思想的核心,對于next[j]的求法,我們采用遞推法,現在我們知道了

next[j]=k,我們來求next[j+1]=?的問題?其實也就是兩種情況:

①:pk=pj 時  則p0p1...pk=pj-kpj-k+1...pj, 則我們知:

              next[j+1]=k+1。

    又因為next[j]=k,則

             next[j+1]=next[j]+1。

②:pk!=pj 時  則p0p1...pk!=pj-kpj-k+1...pj,這種情況我們有點蛋疼,其實這裡我們又将模式串的比對問題轉化為了上面我們提到

的”主串“和”模式串“中尋找next的問題,你可以了解成在模式串的字首串和字尾串中尋找next[j]的問題。現在我們的思路就是一定

要找到這個k2,使得pk2=pj,然後将k2代入①就可以了。

 設   k2=next[k]。 則有p0p1...pk2-1=pj-k2pj-k2+1...pj-1。

 若   pj=pk2,      則 next[j+1]=k2+1=next[k]+1。

 若   pj!=pk2,      則可以繼續像上面遞歸的使用next,直到不存在k2為止。

好,下面我們上代碼,可能有點繞,不管你懂沒懂,反正我懂了。

經典算法題每日演練——第七題 KMP算法

繼續閱讀