[Algorithm] 字符串匹配算法——KMP算法

1 字符串匹配

  字符串匹配是計算機的基本任務之一。

  字符串匹配是什么?舉例來說,有一個字符串"BBC ABCDAB ABCDABCDABDE",我想知道,里面是否包含另一個字符串"ABCDABD"?

  許多算法可以完成這個任務,Knuth-Morris-Pratt算法(簡稱KMP)是最常用的之一。它以三個發明者命名,起頭的那個K就是著名科學家Donald Knuth(《計算機程序設計藝術》的作者)。

2 KMP算法

  這個算法不太容易理解,網上有很多解釋,但讀起來都很費勁。直到讀到Jake Boxer的文章,我才真正理解這種算法。下面,我用自己的語言,試圖寫一篇比較好懂的KMP算法解釋。

  1.

  首先,字符串"BBC ABCDAB ABCDABCDABDE"的第一個字符與搜索詞"ABCDABD"的第一個字符,進行比較。因為B與A不匹配,所以搜索詞后移一位。

  2.

  因為B與A不匹配,搜索詞再往后移。

  3.

  就這樣,直到字符串有一個字符,與搜索詞的第一個字符相同為止。

  4.

  接著比較字符串和搜索詞的下一個字符,還是相同。

  5.

  直到字符串有一個字符,與搜索詞對應的字符不相同為止。

  6.

  這時,最自然的反應是,將搜索詞整個后移一位,再從頭逐個比較。這樣做雖然可行,但是效率很差,因為你要把"搜索位置"移到已經比較過的位置,重比一遍。

  7.

  一個基本事實是,當空格與D不匹配時,你其實知道前面六個字符是"ABCDAB"。KMP算法的想法是,設法利用這個已知信息,不要把"搜索位置"移回已經比較過的位置,繼續把它向后移,這樣就提高了效率。

  8.

  怎么做到這一點呢?可以針對搜索詞,算出一張《部分匹配表》(Partial Match Table)。這張表是如何產生的,后面再介紹,這里只要會用就可以了。

  9.

  已知空格與D不匹配時,前面六個字符"ABCDAB"是匹配的。查表可知,最后一個匹配字符B對應的"部分匹配值"為2,因此按照下面的公式算出向后移動的位數:

  移動位數 = 已匹配的字符數 - 對應的部分匹配值

  因為 6 - 2 等于4,所以將搜索詞向后移動4位。

  10.

  因為空格與C不匹配,搜索詞還要繼續往后移。這時,已匹配的字符數為2("AB"),對應的"部分匹配值"為0。所以,移動位數 = 2 - 0,結果為 2,于是將搜索詞向后移2位。

  11.

  因為空格與A不匹配,繼續后移一位。

  12.

  逐位比較,直到發現C與D不匹配。于是,移動位數 = 6 - 2,繼續將搜索詞向后移動4位。

  13.

  逐位比較,直到搜索詞的最后一位,發現完全匹配,于是搜索完成。如果還要繼續搜索(即找出全部匹配),移動位數 = 7 - 0,再將搜索詞向后移動7位,這里就不再重復了。

  14.

  下面介紹《部分匹配表》是如何產生的。

  首先,要了解兩個概念:"前綴"和"后綴"。 "前綴"指除了最后一個字符以外,一個字符串的全部頭部組合;"后綴"指除了第一個字符以外,一個字符串的全部尾部組合。

  15.

  "部分匹配值"就是"前綴"和"后綴"的最長的共有元素的長度。以"ABCDABD"為例,

  - "A"的前綴和后綴都為空集,共有元素的長度為0;

  - "AB"的前綴為[A],后綴為[B],共有元素的長度為0;

  - "ABC"的前綴為[A, AB],后綴為[BC, C],共有元素的長度0;

  - "ABCD"的前綴為[A, AB, ABC],后綴為[BCD, CD, D],共有元素的長度為0;

  - "ABCDA"的前綴為[A, AB, ABC, ABCD],后綴為[BCDA, CDA, DA, A],共有元素為"A",長度為1;

  - "ABCDAB"的前綴為[A, AB, ABC, ABCD, ABCDA],后綴為[BCDAB, CDAB, DAB, AB, B],共有元素為"AB",長度為2;

  - "ABCDABD"的前綴為[A, AB, ABC, ABCD, ABCDA, ABCDAB],后綴為[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的長度為0。

  16.

  "部分匹配"的實質是,有時候,字符串頭部和尾部會有重復。比如,"ABCDAB"之中有兩個"AB",那么它的"部分匹配值"就是2("AB"的長度)。搜索詞移動的時候,第一個"AB"向后移動4位(字符串長度-部分匹配值),就可以來到第二個"AB"的位置。

  算法時間復雜度為O(m+n)(其中m為字符段長度,n為匹配模式的長度)。

3 算法實現

void getNext(const std::string &p, std::vector<int> &next)
{next.resize(p.size());next[0] = -1;int i = 0, j = -1;while (i != p.size() - 1){//這里注意,i==0的時候實際上求的是next[1]的值,以此類推if (j == -1 || p[i] == p[j]){++i;++j;next[i] = j;}else{j = next[j];}}
}int kmp(const std::string& s, const std::string& p, const int sIndex = 0)
{std::vector<int>next(p.size());getNext(p, next);//獲取next數組,保存到vector中int i = sIndex, j = 0;while(i != s.length() && j != p.length()){if (j == -1 || s[i] == p[j]){++i;++j;}else{j = next[j];}}return j == p.length() ? i - j: -1;
}

?  相關內容:kmp算法實現原理及簡單示例。

轉載于:https://www.cnblogs.com/maybe2030/p/4633153.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/375763.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/375763.shtml
英文地址,請注明出處:http://en.pswp.cn/news/375763.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

入門Git

本文是我在[實驗樓]這個平臺學習git時的第一篇學習筆記&#xff0c;現貼出來以饗大家&#xff01; git學習 1、git的配置 git的配置主要通過git config --global <配置名稱> <配置的值>來對git進行配置 其中最常用的配置為&#xff1a; git config --global u…

小程序廣告變現:探索創新路徑實現盈利

隨著移動互聯網的不斷發展&#xff0c;小程序作為一種輕量級應用形式&#xff0c;在用戶中的普及程度不斷提升。對于開發者而言&#xff0c;如何在小程序中實現盈利成為了一項挑戰&#xff0c;而廣告變現成為其中一種常見的經濟模式。本文將深入探討小程序廣告變現的方式以及如…

服務器共享文件審計,內網安全管理系統-共享審計

在現代企事業單位的網絡中&#xff0c;最常用的功能莫過于“共享文件”了。財務部門需要當月員工的考勤信息&#xff0c;人事部門可能不會親自拿過去&#xff0c;而是在網絡上共享&#xff1b;生產部門的生產報表也不會用書面的資料分發&#xff0c;而是放在網絡的共享文件夾下…

介紹“Razor”— ASP.NET的一個新視圖引擎

我的團隊當前正在從事的工作之一就是為ASP.NET添加一個新的視圖引擎。 一直以來&#xff0c;ASP.NET MVC都支持 “視圖引擎”的概念—采用不同語法的模板的可插拔模塊。當前ASP.NET MVC “默認”的視圖引擎是ASP.NET Web窗體使用的.aspx/.ascx/.master文件模板。而當今其他一些…

w10系統打印服務器怎樣出來,win10怎么打開關閉打印機服務教程步驟

當我們想要使用打印機服務時候&#xff0c;卻不知道在哪里打開&#xff0c;對于win10系統&#xff0c;具體怎么操作呢?下面小編來告訴你開啟和關閉打印機服務的方法吧&#xff0c;希望對你有幫助!Win10系統開啟和關閉打印機服務的方法1、在Win10系統下&#xff0c;按住鍵盤的“…

獲取函數的名字

c99標準中的__func__預定義標識符功能可以幫我們獲取函數的名稱 #include<string> #include<iostream> using namespace std;const char *hello(){return __func__; }int main(){cout<<hello()<<endl;return 0; }代碼中的函數相當于&#xff1a; con…

淺談自學方法論- 不斷更新-記錄思路

1. 用程序員的思想&#xff0c;去自學。 從主函數入手&#xff0c;也就是&#xff0c;了解整個框架。 2. 讀書&#xff0c;帶著宏觀和微觀的思路&#xff0c; 先不管看得懂看不懂看第一遍&#xff0c; 然后帶著問題去讀第二遍&#xff0c;并搜索不懂得關鍵詞。 第三遍&#xff…

xp系統目前禁用索引服務器,WinXP系統中可以被禁用的服務對照表

application layer gateway service為internet連接共享和internet連接防火墻提供第三方協議插件的支持如果你沒啟用internet連接共享或windows xp內置防火墻&#xff0c;可以禁止這個服務。automatic updates自動從windows update啟用windows更新的下載和安裝需要時&#xff0c…

hadoop之linux常用命令

Linux的命令后面會有命令選項&#xff0c;有的選項還有選項值。選項的前面有短橫線“-”&#xff0c;命令、選項、選項值之間使用空格隔開。有的命令沒有選項&#xff0c;會有參數。選項是命令內置的功能&#xff0c;參數是用戶提供的符合命令格式的內容。 1.1.1. 命…

c獲取文件的名字和運行到程序的第幾行功能

可以通過__FILE__和__LINE__兩個宏獲取文件的名字和代碼運行的行數 #include<stdio.h> int main(){printf("file:%s line:%d\n",__FILE__,__LINE__);return 0; }__FILE__在linux中能獲取到文件名稱&#xff0c;但是在windows中獲取的是帶路徑的名字。

MongoDB系列二

簡介 MongoDB是一個基于分布式文件存儲的數據庫。由C語言編寫。旨在為WEB應用提供可擴展的高性能數據存儲解決方案。 MongoDB是一個高性能&#xff0c;開源&#xff0c;無模式的文檔型數據庫&#xff0c;是當前NoSql數據庫中比較熱門的一種。 MongoDB是一個介于關系數據庫和非關…

通過查看__cplusplus的值查看編譯器的C++標準

C03標準中&#xff0c;__cplusplus被定義為199711L&#xff0c;而在C11中&#xff0c;__clpusplus則被定義為201103L #include<iostream> using namespace std; int main(){cout<<__cplusplus<<endl;return 0; }

Oracle-數據實現豎排打印

--存放重證評分的數據表create table ZZPFapache2( ZZ_datetime DATE, --時間 ZZ_zongfen INTEGER, --總分 ZZ_shiwanglui INTEGER, --死亡率 ZZ_BINGRENID VARCHAR2(50), --病人ID ZZ_h1f1 INTEGER, --第1行1個分 ZZ_h1m1 VARCHAR2(40), ZZ_h1f2 INTEGER, --第1行…

C#時間格式

可以這樣寫: date.ToString("yyyy年MM月", DateTimeFormatInfo.InvariantInfo) 日期轉化二 DateTime dt DateTime.Now; Label1.Text dt.ToString();//2005-11-5 13:21:25 Label2.Text dt.ToFileTime().ToString();//127756416859912816 Label3.Text dt.ToFileTim…

C++11的靜態斷言

斷言就是將一個返回值總是需要為真的判別式放在語句中&#xff0c;來排除在設計的邏輯上不應該出現的情況。C11標準中引入了靜態斷言&#xff1a;static_assert 在C標準中&#xff0c;<cassert>或assert.h為我們提供了assert宏&#xff0c;但是這個宏只有在運行時才進行…

C++ 字符串編程訓練2

今天講的一道習題是很經典的約瑟夫環問題&#xff0c;其實lz對于鏈表的某些操作還不是太懂&#xff0c;所以在程序中有些地方還不太看得懂&#xff0c;這里借鑒的網上的做法&#xff0c;還請大牛能夠解答我的疑惑&#xff0c;謝謝&#xff01; 標題&#xff1a;約瑟夫環 說明&a…

linux擴展lvm磁盤

env&#xff1a; centos 6.5 x64 hyper-v虛擬機 這個方法可以在當前運行的系統中擴展root磁盤 詳細步驟 之前想創建的一個虛擬機的磁盤空間不夠用了&#xff0c;所以想擴容一下磁盤。 正好使用的時候是lvm磁盤&#xff0c;可以支持擴容。 格式化一個新的分區或者磁盤 Command…

C/C++編譯、測試須知、須會,CMake、Boost等

以下內容為本人實習期間學習筆記&#xff01;&#xff01;參考了網上的許多教程&#xff0c;共享大家&#xff0c;歡迎交流。 動態庫和靜態庫&#xff08;共享庫&#xff09; 不同點&#xff1a;代碼被載入的時刻不同 靜態庫的代碼在編譯過程中已經被載入可執行程序&#xf…

C# DataTable去除重復,極其簡便、簡單

其中sourceDT是獲取到的一個DataTable類型的集合對象 去重復使用方式&#xff1a; 實例化一個DataView對象 假設為dv&#xff0c;直接dv.ToTable()即可&#xff0c;ToTable中可為&#xff08;true,"用于判斷重復的列"&#xff09;&#xff0c;比如圖中所示&#xff0…

【轉】C++類中對同類對象private成員訪問

私有成員變量的概念&#xff0c;在腦海中的現象是&#xff0c;以private關鍵字聲明&#xff0c;是類的實現部分&#xff0c;不對外公開&#xff0c;不能在對象外部訪問對象的私有成員變量&#xff0e; 然而&#xff0c;在實現拷貝構造函數和賦值符函數時&#xff0c;在函數里利…