日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

KMP 算法详解(CPP 实现)

發(fā)布時間:2024/3/26 编程问答 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 KMP 算法详解(CPP 实现) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

轉(zhuǎn)載請標(biāo)明出處:https://blog.csdn.net/kiss0tql/article/details/81416283
本文來自:deemo的博客

  • 說明
  • kmp 算法思想
  • next 數(shù)組計算
  • 字符串匹配
  • 參考資料

說明

??kmp 算法是由 Knuth、Morris 和 Pratt 三人設(shè)計的一個非常高效的字符串匹配算法。該算法用于判斷一個字符串是否包含于另一個字符串中。該算法復(fù)雜度是 O(m+n)O(m+n),其中 mmnn 分別是兩個字符串的長度。

kmp 算法思想

??我們能很容易想到 O(M?N)O(M?N) 復(fù)雜度的算法,即索引從字符串 str1 的第一個字符開始,依次檢查以該字符作為首字符的連續(xù) N 個字符是否與字符串 str2 匹配,若不匹配,則索引向前移動一位,從第二個字符開始依次檢查,以此類推。

??該算法并沒有考慮到現(xiàn)已匹配的部分字符串的結(jié)構(gòu)。已匹配的部分字符串中可能包含相同的部分,若利用這些相同的部分,我們可以一次性移動多位而不只是移動一位,這樣大大提高了算法的效率。

??假設(shè)現(xiàn)有兩個字符串 str1 和 str2,并且現(xiàn)已從 index1 處開始檢是否匹配。當(dāng)我們檢測到 index2 處時,發(fā)現(xiàn)此時待檢測的兩個字符不相同(即圖中 A 和 B 部分字符不匹配,index2 和 index1 之間的字符均匹配),按照傳統(tǒng)思想我們將會從 index1 + 1 處的位置開始新一輪的檢測,這樣檢測的效率極低。當(dāng)我們仔細(xì)觀察現(xiàn)已匹配的部分字符串時,若能找到字符串中首尾相同的部分(即圖中 1、2、3和4部分的字符串相同),則可以直接將 str2 向前移動 k 個位置(如圖所示),即圖中 2 和 4 部分將作為新匹配字符串的頭部,并接著從 str1 的 index2 位置開始檢測。若不含有相同的部分,此時不管是將 str2 向前移動多少位都不會出現(xiàn)匹配的字符串,因此,可以從 str1 的 index2 位置開始新的一輪檢測(即從 str2 的第一個字符開始匹配,可以理解為將 str2 向前移動了 str2 長度的位置)。

??現(xiàn)有如下兩個字符串:

const char *str1 = "bacbbacabadababacambabacadbacabacasdsd";const char *str2 = "bacabaca";

??按照上述的匹配過程可以得到如下的匹配過程圖,當(dāng)匹配到 index2 處時,發(fā)現(xiàn)字符 d 和 字符 c 不匹配,但已匹配的部分 bacaba 的首位相同的部分為 ba,該部分為字符串 bacaba 的最長的相同前綴和后綴,而在下一輪的匹配過程中,將 ba 部分作為新的首部,并接著 index2 處開始匹配。一次類推完成整個字符串的匹配過程。

??由以上匹配過程可知,每當(dāng)開始新一輪的匹配時,我們需要知道當(dāng)前已匹配的字符串中最長相同前綴和后綴的長度,以實現(xiàn)匹配過程中的快速跳轉(zhuǎn)。而當(dāng)前已匹配的字符串長度可能為 1 ~ length(str2),因此,我們可以創(chuàng)建一個長度為 n 的數(shù)組 ( n = length(str2) ),用來保存不同長度字符串的最長相同前綴和后綴的長度。該數(shù)組為 next 數(shù)組。

next 數(shù)組計算

??next 數(shù)組中第 i 個數(shù)表示字符串索引從 0 到 i 的字符串最長相同前綴和后綴的長度。注意,前綴和后綴不包括字符串本身,比如 aaaa 相同的最長前綴和最長后綴是 aaa 。
??假設(shè)我們現(xiàn)在已經(jīng)求得 next[0]、next[1]、…… next[ i-1 ],分別表示長度為 1 到 i 的字符串的最長相同前綴和后綴的長度。現(xiàn)在要求 next[ i ]。由下圖我們可以看到,如果位置 i 和位置 next[ i-1 ] 處的兩個字符相同(下標(biāo)從零開始),則 next[ i ]等于 next[ i-1 ] + 1。

??如果兩個位置的字符不相同,我們可以將長度為 next[ i-1 ] 的字符串繼續(xù)分割,獲得其最大公共長度 next[ next[ i-1 ] -1 ],然后再和位置 i 的字符比較。由于長度為 next[ i-1 ] 的字符串的公共長度保存在索引為 next[ i-1 ] -1 的 next 數(shù)組中,而該字符串又包含相同的前綴和后綴,如下圖所示,如果位置 next[ next[ i-1 ] -1 ]和位置 i 的字符相同,則 next [i ]就等于 next[ next[ i -1 ] -1 ] + 1。如果不相等,就可以繼續(xù)分割長度為 next[ next[ i -1 ] -1 ] 的字符串,直到字符串長度為 0 為止。

??根據(jù)上述計算 next 數(shù)組的過程,可以寫出相應(yīng)的求 next 數(shù)組的代碼(CPP實現(xiàn)):

vector<int> getNext(const char *str) {int len = strlen(str); // 字符串長度vector<int> next(len, 0); // 保存結(jié)果,next[0]=0for(int i = 1; i < len; i++){int k = next[i - 1]; // k 表示需要比較的位置,初始值為 next[i - 1]while(k > 0 && str[i] != str[k]) // 比較,若不相等則繼續(xù)分割,直到相等或為0(即不含相同部分)k = next[k - 1];if(str[i] == str[k]) // 若相等,則 next[i] = k + 1,否則為0,其中 k 為索引k++;next[i] = k; // 更新 next[i]}return next; }

??在求解 next 數(shù)組后,可以利用該數(shù)組進行字符串匹配。

字符串匹配

??假若當(dāng)前字符串 str1 的第 i 個字符正在與字符串 str2 的第 k 個字符進行匹配(i 和 k 均從 0 開始),若相等,則進行下一輪的匹配,即進行 str1 的 第 i+1 個字符與 str2 的第 k+1 個字符匹配;若不相等,則跟 str2 的第 next[k - 1] 字符進行匹配,直到 k = 0 為止。重復(fù)以上過程直到檢測完 str1 的所有字符。

??根據(jù)上述匹配過程,完成 CPP 代碼:

int search(const char *str1, const char *str2) {vector<int> next = getNext(str2); // 獲得 str2 的 next 數(shù)組int k = 0; // 記錄當(dāng)前已匹配 str2 的索引int res = -1; // 保存匹配的字符串起始位置,若不存在,返回-1for(int i = 0; i < (int)strlen(str1); i++) // 第 i 輪匹配{while(k > 0 && str1[i] != str2[k]) // str1的第i個與str2的第k個字符進行比較,若不同,則k=next[k-1],直到k為0或相等為止k = next[k - 1];if(str1[i] == str2[k]) // 若相等,更新kk++;if(k == (int)strlen(str2)) // 若找到完全匹配{res = i - k + 1; // 保存匹配的字符串起始位置,此時根據(jù)需要可用容器保存多個結(jié)果k = next[k - 1]; // 進行下一輪匹配,此處根據(jù)需要可去掉}}return res; }

??以上就是 KMP 算法的整個過程,完整的代碼可訪問 Github 下載。

參考資料

  • KMP算法學(xué)習(xí)(詳解)
  • KMP算法最淺顯理解 —— 一看就明白

以上內(nèi)容已同步至 Github。

總結(jié)

以上是生活随笔為你收集整理的KMP 算法详解(CPP 实现)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。