KMP算法詳解

2021-03-02 信息學競賽

       kmp算法又稱「kmp」算法,是一個效率非常高的字符串匹配算法。不過由於其難以理解,所以在很長的一段時間內一直沒有搞懂。雖然網上有很多資料,但是鮮見好的博客能簡單明了地將其講清楚。在此,綜合網上比較好的幾個博客(參見最後),儘自己的努力爭取將kmp算法思想和實現講清楚。

kmp算法完成的任務是:給定兩個字符串O和f,長度分別為n和m,判斷f是否在O中出現,如果出現則返回出現的位置。常規方法是遍歷a的每一個位置,然後從該位置開始和b進行匹配,但是這種方法的複雜度是O(nm)。kmp算法通過一個O(m)的預處理,使匹配的複雜度降為O(n+m)。

kmp算法思想

        我們首先用一個圖來描述kmp算法的思想。在字符串O中尋找f,當匹配到位置i時兩個字符串不相等,這時我們需要將字符串f向前移動。常規方法是每次向前移動一位,但是它沒有考慮前i-1位已經比較過這個事實,所以效率不高。事實上,如果我們提前計算某些信息,就有可能一次前移多位。假設我們根據已經獲得的信息知道可以前移k位,我們分析移位前後的f有什麼特點。我們可以得到如下的結論:

A段字符串是f的一個前綴。

B段字符串是f的一個後綴。

A段字符串和B段字符串相等。

    所以前移k位之後,可以繼續比較位置i的前提是f的前i-1個位置滿足:長度為i-k-1的前綴A和後綴B相同。只有這樣,我們才可以前移k位後從新的位置繼續比較。


        所以kmp算法的核心即是計算字符串f每一個位置之前的字符串的前綴和後綴公共部分的最大長度(不包括字符串本身,否則最大長度始終是字符串本身)。獲得f每一個位置的最大公共長度之後,就可以利用該最大公共長度快速和字符串O比較。當每次比較到兩個字符串的字符不同時,我們就可以根據最大公共長度將字符串f向前移動(已匹配長度-最大公共長度)位,接著繼續比較下一個位置。事實上,字符串f的前移只是概念上的前移,只要我們在比較的時候從最大公共長度之後比較f和O即可達到字符串f前移的目的。


next數組計算

            理解了kmp算法的基本原理,下一步就是要獲得字符串f每一個位置的最大公共長度。這個最大公共長度在算法導論裡面被記為next數組。在這裡要注意一點,next數組表示的是長度,下標從1開始;但是在遍歷原字符串時,下標還是從0開始。假設我們現在已經求得next[1]、next[2]、……next[i],分別表示長度為1到i的字符串的前綴和後綴最大公共長度,現在要求next[i+1]。由上圖我們可以看到,如果位置i和位置next[i]處的兩個字符相同(下標從零開始),則next[i+1]等於next[i]加1。如果兩個位置的字符不相同,我們可以將長度為next[i]的字符串繼續分割,獲得其最大公共長度next[next[i]],然後再和位置i的字符比較。這是因為長度為next[i]前綴和後綴都可以分割成上部的構造,如果位置next[next[i]]和位置i的字符相同,則next[i+1]就等於next[next[i]]加1。如果不相等,就可以繼續分割長度為next[next[i]]的字符串,直到字符串長度為0為止。由此我們可以寫出求next數組的代碼:

public int[] getNext(String b)  

{  

    int len=b.length();  

    int j=0;  

          

    int next[]=new int[len+1];  

    next[0]=next[1]=0;  

          

    for(int i=1;i<len;i++)  

    {  

        while(j>0&&b.charAt(i)!=b.charAt(j))j=next[j];  

        if(b.charAt(i)==b.charAt(j))j++;  

        next[i+1]=j;  

    }  

          

    return next;  

}  

        上述代碼需要注意的問題是,我們求取的next數組表示長度為1到m的字符串f前綴的最大公共長度,所以需要多分配一個空間。而在遍歷字符串f的時候,還是從下標0開始(位置0和1的next值為0,所以放在循環外面),到m-1為止。代碼的結構和上面的講解一致,都是利用前面的next值去求下一個next值。

字符串匹配

計算完成next數組之後,我們就可以利用next數組在字符串O中尋找字符串f的出現位置。匹配的代碼和求next數組的代碼非常相似,因為匹配的過程和求next數組的過程其實是一樣的。假設現在字符串f的前i個位置都和從某個位置開始的字符串O匹配,現在比較第i+1個位置。如果第i+1個位置相同,接著比較第i+2個位置;如果第i+1個位置不同,則出現不匹配,我們依舊要將長度為i的字符串分割,獲得其最大公共長度next[i],然後從next[i]繼續比較兩個字符串。這個過程和求next數組一致,所以可以匹配代碼如下:

public void search(String original, String find, int next[]) {  

    int j = 0;  

    for (int i = 0; i < original.length(); i++) {  

        while (j > 0 && original.charAt(i) != find.charAt(j))  

            j = next[j];  

        if (original.charAt(i) == find.charAt(j))  

            j++;  

        if (j == find.length()) {  

            System.out.println("find at position " + (i - j));  

            System.out.println(original.subSequence(i - j + 1, i + 1));  

            j = next[j];  

        }  

    }  

}  

上述代碼需要注意的一點是,每次我們得到一個匹配之後都要對j重新賦值。

複雜度

kmp算法的複雜度是O(n+m),可以採用均攤分析來解答,具體可參考算法導論。

相關焦點

  • KMP算法文章合集
    –KMP算法理解(python) KMP的next數組求法詳解 KMP算法詳解 KMP算法(代碼+圖解證明) 算法之字符串匹配的KMP算法1 KMP算法解決字符串匹配問題 字符串的模式匹配(KMP)算法 字符串匹配KMP算法的理解(詳細) python實現kmp算法(學不會你噴我)
  • 動態規劃之 KMP 算法詳解
    KMP 算法(Knuth-Morris-Pratt 算法)是一個著名的字符串匹配算法,效率很高,但是確實有點複雜。很多讀者抱怨 KMP 算法無法理解,這很正常,想到大學教材上關於 KMP 算法的講解,也不知道有多少未來的 Knuth、Morris、Pratt 被提前勸退了。
  • 教你初步了解KMP算法
    2.2、kmp算法有了覆蓋函數,那麼實現kmp算法就是很簡單的了,我們的原則還是從左向右匹配,但是當失配發生時,我們不用把target_index向回移動,target_index前面已經匹配過的部分在pattern自身就能體現出來,只要動pattern_index就可以了。
  • 漫畫:什麼是KMP算法?
    ,利用哈希值進行比較的RK算法,以及儘量減少比較次數的BM算法,沒看過的小夥伴可以點擊下方連結:1.BF算法和RK算法2. BM算法如果沒時間細看也沒關係,就讓我帶著大家簡單梳理一下。首先,給定 「主串」 和 「模式串」 如下:BF算法是如何工作的?
  • KMP算法圖文詳解
    KMP算法是解決字符串匹配的常用算法之一,也就是在主串(比如aabbccdd)中的子串(bc)定位問題。子串稱為P,如果它在一個主串稱為T中出現,就返回它的具體位置,我們先來看看普通的字符串匹配是怎麼做的最基礎的匹配思路:從左到右一個個匹配,如果這個過程中有某個字符不匹配,將子串向右移動一位,繼續從左到右一一匹配。
  • 讓你搞懂到底什麼是KMP算法
    這個時候,KMP算法橫空出世圖片中的內容截取於維基百科說了這麼多,這個算法究竟是如何進行字符串查找的呢?首先,該算法需要在開始匹配之前收集足夠的「信息」,用於在匹配過程中給出「指導意見」這個「信息」指,根據待查找字符串T建立的一張表
  • 輕鬆理解什麼是KMP算法
    KMP算法 內部涉及到的數學原理與知識太多,本文只會對 KMP算法 的運行過程、 部分匹配表 、next數組 進行介紹,如果理解了這三點再去閱讀其它有關 KMP算法 的文章肯定能有個清晰的認識。以下的文字描述請結合視頻動畫來閱讀~定義Knuth-Morris-Pratt 字符串查找算法,簡稱為 KMP算法,常用於在一個文本串 S 內查找一個模式串 P 的出現位置。
  • EM算法詳解
    EM算法簡介3. 預備知識    3.1 極大似然估計    3.2 Jensen不等式4. EM算法詳解    4.1 問題描述    4.2 EM算法推導流程    4.3 EM算法流程5.EM算法若干思考    5.1 對EM算法的初始化研究    5.2 EM算法是否一定收斂?    5.3 如果EM算法收斂,能否保證收斂到全局最大值?6. EM算法實例7. 對EM算法總結1.
  • 常用算法詳解——列印楊輝三角形
    在中國南宋數學家楊輝1261年所著的《詳解九章算法》一書中出現。在歐洲,這個表叫做帕斯卡三角形。帕斯卡(1623----1662)是在1654年發現這一規律的,比楊輝要遲393年,比賈憲遲600年。楊輝三角是中國古代數學的傑出研究成果之一,它把二項式係數圖形化,把組合數內在的一些代數性質直觀地從圖形中體現出來,是一種離散型的數與形的結合。
  • 密碼學_RSA算法原理詳解
    RSA算法簡介:     RSA算法 是一種公鑰加密算法,RSA算法相比別的算法思路非常清晰,但是想要破解的難度非常大
  • 機器學習-聚類算法k-均值詳解
    對於含有n個數據的數據集D,以及簇數k,本文所講的劃分算法將基於距離函數,將對象組劃分成k個分區,每個分區代表一個簇,並儘量使簇中對象相似,不同簇中對象相異。使用簇內變差來衡量Ci的質量,它是Ci中所有對象和形心直接的誤差的平方和:定義E為數據集中所有對象的誤差平方和:算法原理為了得到k個簇,k-均值算法首先在D中隨機的選取k個對象,作為k個簇的中心。對於剩下的每個對象,根據其與各個簇中心的歐式距離,將其分配到最近的一個簇中。
  • RSA算法詳解
    RSA算法用到的數學知識特別多,所以在中間介紹這個算法生成私鑰和公鑰的過程中會穿插一些數學知識。生成步驟如下:1.尋找兩個不相同的質數隨意選擇兩個大的質數p和q,p不等於q,計算N=p*q;什麼是質數?
  • 目標檢測算法YOLO-V1算法詳解
    ❝前面我們一起學了SSD算法的相關知識,如下:SSD目標檢測算法必須知道的幾個關鍵點目標檢測算法SSD結構詳解❞
  • 目標檢測算法YOLO-V2詳解
    ❝上期我們一起學習了YOLO-V1算法的框架原來和損失函數等知識,如下:目標檢測算法YOLO-V1算法詳解目標檢測模型YOLO-V1損失函數詳解【文末領福利】❞今天,我們一起學習下YOLO-V2跟YOLO-V1比起來都做了哪些改進?
  • NLP技術路線詳解:這是從數學到算法的藝術
    選自Github項目作者:Tae-Hwan Jung機器之心編譯自然語言處理路線圖詳解,從數學基礎、語言基礎到模型和算法,這是你該了解的知識領域。自然語言處理很多時候都是一門綜合性的學問,它遠遠不止機器學習算法。相比圖像或語音,文本的變化更加複雜,例如從預處理來看,NLP 就要求我們根據對數據的理解定製一種流程。而且相比圖像等更偏向感知的智能,自然語言包含更高一級的智能能力,不論是承載思想、情感還是推理。那麼我們該怎樣學習自然語言處理,有什麼比較好的路線嗎?
  • 京東退貨運費怎麼算 費用誰承擔與算法詳解
    京東物流作為目前比較快捷的快遞方式,發貨的時候通過自建倉配物流網絡大方式為用戶自動匹配最近的倉庫,在退貨的時候卻需要自行寄回,相應的運費也會有所不同,下面就跟小編了解下具體算法吧。 原標題:京東退貨運費怎麼算 退貨費用算法詳解 責任編輯:曾少林
  • 蟻群算法即相關代碼實現詳解—matlab之智能算法
    蟻群算法即相關代碼實現詳解 一.算法背景 蟻群算法是近年來剛剛誕生的隨機優化方法,它是一種源於大自然的新的仿生類算法.由義大利學者Dorigo最早提出,螞蟻算法主要是通過螞蟻群體之間的信息傳遞而達到尋優的目的,最初又稱蟻群優化方法(Ant Colony
  • 【每月好書】OpenCV算法精解
    -割--OpenCV算法精解:基於Python與C++(系統闡述OpenCV基本概念、數學原理、C++和Python實現!)本書既注重基本的概念理論及數學原理,也注重其代碼實現及實際應用,力求幫助讀者全面系統地掌握圖像算法的基本技術,同時為掌握OpenCV 打下良好的基礎。《OpenCV算法精解:基於Python與C++》適合入門圖像處理和計算機視覺領域的初學者閱讀,要求讀者具備一定的C++ 或Python 編程基礎。
  • OpenCV中直方圖反向投影算法詳解與實現
    OpenCV中直方圖反向投影算法詳解與實現一:直方圖交叉OpenCV中直方圖反向投影算法實現來自一篇論文《Indexing Via Color
  • 機器學習算法的特徵工程與意義詳解
    機器學習算法的特徵工程與意義詳解 黃言 發表於 2020-10-08 15:24:00 1、特徵工程與意義 特徵就是從數據中抽取出來的對結果預測有用的信息