KMP

看數(shù)據(jù)結構的書,字符串章節(jié)提到這個字符串匹配的算法。結果一看,真是比較難理解,不愧是三個人想出來的算法,以三個人的名字命名這個算法KMP。書上講的也是看不明白,只能上網(wǎng)搜搜比較通俗易懂的回答 。結果大部分都是復制粘貼,提到遞歸什么的,越看越糊涂,估計作者自己都不明白。后來還是在知乎上看到一個點贊數(shù)比較多的,結果一看,講的不錯。知乎不愧是程序員用戶比較多的平臺,大神就是吊。
自己看了看,想了想,動手抄一遍,運行一下,加深記憶理解。

原文地址: 鏈接

直接開始廢話,也都是抄這位作者的,只是為了自己寫一遍

一、問題:定位出一個字符串在另一個字符串中完全匹配的位置。

比如目標字符串:abcabcdee 主字符串:xyzabcabcdeezzz
明顯一看,結果就是3。在第3位置的a開始匹配到最后。
如果用樸素方法,直接遍歷,兩個字符串挨個對比,如果不匹配,目標字符串從頭開開始,主字符串回到上次匹配位置的下一個位置。每次不匹配的時候,都需要從頭開始。最差情況下,主字符串從頭到最后,目標字符串每次都是到最后一個字符不匹配,所以時間復雜度就是O(m*n)。

二、樸素方法的弊端

樸素方法每次匹配失敗的時候都要從頭開始,比如匹配到第6個字符失敗了,如果知道了失敗了的位置之前到字符串,也就是前5個字符的前綴和后綴的交集,就可以從這個交集長度的位置開始下次匹配了,這樣,目標字符串不用從0開始,主字符串也不用回溯到上一次開始的地方了。就節(jié)省了很多步。

三、字符串前綴和后綴的交集

前綴:一個字符串的子字符串,確保包含第一個字符,但不包含自身。
后綴:一個字符串的子字符串,確保包含最后一個字符,但不包含自身。
比如:abab
前綴集合:a、ab、aba。后綴集合:b、ab、bab。
所以集合的交集是ab。交集可能不止一個。需要的是最大長度。
有了前后綴交集的長度,說明可以重疊著么長,既然都重疊了,說明前面重疊部分不需要對比了,直接從重疊的下一個位置開始就行了。

KMP的關鍵就是求目標字符串每個位置的前后綴交集里最大長度。

四、部分匹配表

比如:字符串“abababca”


匹配數(shù)組.jpg

首先,對于這個數(shù)組,最后一個位置的值是用不上的。因為這個表的用處是在匹配失敗的時候需要回溯,回溯位置是前面子字符串的表值+1。
比如目標這個字符串長度是8,匹配到位置6失敗了,這時候需要回溯到前5個子字符串,第5位置的值是2,這時候需要回溯到3。從3位置的字符開始接著匹配。
媽的,亂七八糟,說不清。
所以真正用到的值是當前位置前一個位置的表值,最有一個位置的值只能在全部匹配完的時候才用到,但是全部匹配完意味著匹配成功,找到結果了,更用不上它了。所以最后一個值沒什么卵用。
而且為了編程方便,就把每個位置對應的值往后推了一格,把最后一個扔了,反正也用不上,第一個空了出來,用-1代替。所以一般叫next數(shù)組。匹配失敗的時候,回溯位置也不用上一個位置的值+1了,直接就是自己位置對應的值了。

void getNext(char *p, int *arr) {
    
    int i = 0;
    int j = -1;
    arr[0] = -1;
    
    while (i < strlen(p)-1) {
        if (j == -1 || p[i] == p[j]) {
            I++;
            j++;
            arr[i] = j;
        }else{
            j = arr[j];
        }
    }
}

跟著流程走一下,可以發(fā)現(xiàn),這個匹配表的前兩位固定是-1、0


尋找過程.jpg

i的位置表示自己的后綴字符串,j表示前綴字符串。
這里比較繞,說不清,真雞兒難。該睡覺了。

KMP

int kmpMethod(char *str, char *target, int *next) {
    
    int i = 0;
    int j = 0;
    
    while (i < strlen(str) && j < (int)strlen(target)) {
        if (j == -1 || str[i] == target[j]) {
            I++;
            j++;
        }else{
            j = next[j];
        }
    }
    
    if (j == strlen(target)) {
        return i - j;
    }
    
    return -1;
}

匹配成功,各自往后走,各個位置+1。
如果匹配失敗,就找當前位置的前面子字符串的匹配表的值,意味找最大重合部分,如果有重合部分,就不用比較前面的了,直接從重合部分開始比較后面的。如果當前位置前面子字符串值是0,意味著沒有重合部分,就縮小范圍,尋找上一個子字符串的前后綴重合部分,有的話就開始匹配,沒有的話,就接著尋找上上一個,直到值是-1,意味著當前位置前面的子字符串沒有一個完全沒有重疊的部分,就只能從頭開始,就各自+1,目標字符串從頭開始了,回溯到0,主字符串是一直往后走的,不回溯。
注:strlen得到無符號整型,j的值是-1的時候,會出現(xiàn)-1>無符號數(shù)值的問題,所以需要用int強轉(zhuǎn)一下。

說不明白,哈哈??偟膩碚fKMP算法過程容易理解,求部分匹配表那個算法比較難理解。反正這陣子睡眠不好,剛好記錄一下。想看的時候直接來簡書看,方便。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 字符串匹配KMP算法詳解 1. 引言 以前看過很多次KMP算法,一直覺得很有用,但都沒有搞明白,一方面是網(wǎng)上很少有...
    張晨輝Allen閱讀 2,646評論 0 3
  • 數(shù)據(jù)結構與算法--KMP算法查找子字符串 部分內(nèi)容和圖片來自這三篇文章: 這篇文章、這篇文章、還有這篇他們寫得非常...
    sunhaiyu閱讀 1,883評論 1 21
  • title: 串的模式匹配算法之kmptags: 數(shù)據(jù)結構與算法之美author: 辰砂tj 1.引言 首先我們需...
    tojian閱讀 1,155評論 0 0
  • KMP的由來 在KMP算法之前,對文本進行匹配時使用的是樸素模式匹配算法,也就是最簡單匹配算法.當然運行效率也是讓...
    圣光懺悔閱讀 1,882評論 2 13
  • 參考文章 知乎:如何更好的理解和掌握 KMP 算法?從頭到尾徹底理解KMPKMP 算法(1):如何理解 KMP(原...
    Mjolnir1107閱讀 1,232評論 0 0

友情鏈接更多精彩內(nèi)容