淺談KMP演算法及其next[]數組

来源:http://www.cnblogs.com/Onlynagesha/archive/2016/04/10/5375333.html
-Advertisement-
Play Games

KMP演算法是眾多優秀的模式串匹配演算法中較早誕生的一個,也是相對最為人所知的一個。 演算法實現簡單,運行效率高,時間複雜度為O(n+m)(n和m分別為目標串和模式串的長度),比蠻力演算法的O(nm)快了許多。 理解KMP演算法,關鍵是理解其中的精髓——next[]數組。 (統一起見,下文將目標字元串記作ob ...


KMP演算法是眾多優秀的模式串匹配演算法中較早誕生的一個,也是相對最為人所知的一個。

演算法實現簡單,運行效率高,時間複雜度為O(n+m)(n和m分別為目標串和模式串的長度),比蠻力演算法的O(nm)快了許多。

理解KMP演算法,關鍵是理解其中的精髓——next[]數組。

 

(統一起見,下文將目標字元串記作obj,將模式字元串記作pattern,這與後面的程式代碼是一致的)

我們給一個字元串S定義一個next值,記作next(S),next(S)=n表示:

(1)S的前n個字元構成的首碼,和後n個字元的尾碼是相等的

(2)n是滿足(1)條件的極大值。

如果不存在一個滿足(1)條件的n,那麼記next(S)=0。

例如,字元串“GACC”的next值為0,“GACCGG”的next值為1(極大前(後)綴為“G"),

“GACCGGACC”的next值為4(極大前(後)綴為”GACC“),“GAGAG”的next值為5。(極大前(後)綴就是它本身)。

而next[]數組,記錄的則是pattern的所有首碼的next值。

 

next數組的作用,就是在模式匹配的過程中,儘可能減少模式串的偏移量。

下令obj=”GACGAACGACCGACGACGCCGACGAC“(O__O"…),pattern=”GACGCCG“。

模式匹配的流程如下:

設立兩個”游標“i和j,分別指向obj串和pattern串當前正在檢查的字元,初始時令i=j=0。

首先檢查第一個字元,若obj[i]==pattern[j],那麼i++,j++。直到遇到obj[i]!=pattern[j]的情形。

GACGAACGACCGACGACGCCGACGAC

GACGCCG

前4個字元檢查通過,但第5個字元(i=j=4)出了問題。

朴素的做法是讓i和j都回退,對於此例,回退至i=1,j=0,然而這樣無疑會做許多重覆的計算。

而KMP演算法的做法則是:只移動pattern。而移動的方法,則關係到演算法的效率甚至正確性。

我們的原則是:保證正確的前提下,使得重覆判斷的次數儘可能少。

最佳的做法是將j移動到next[j-1]的位置。

(1)由next的性質(首碼等於尾碼的極大值)可知,這樣可以省去儘可能多的判斷

(2)並且可以保證這樣做是正確的

此時j=4,而next[j-1]=next[3]=1,所以令j=1,再次進行比較。

GACGAACGACCGACGACGCCGACGAC

------GACGCCG

比對通過。令i=5,j=2。

GACGAACGACCGACGACGCCGACGAC

------GACGCCG

next[1]=0,所以令j=0。

GACGAACGACCGACGACGCCGACGAC

----------GACGCCG

即使回退到第一位也沒有出現字元相等的情形。而我們已經無法回退了。

此時只能令i++,表示前面的部分檢查無果,繼續向後檢查。

(你也可以理解成,讓obj相對於pattern運動)

GACGAACGACCGACGACGCCGACGAC

--------------GACGCCG

重覆上述的過程到了這裡(i=10,j=3),next[2]=0

GACGAACGACCGACGACGCCGACGAC

--------------------GACGCCG

pattern無法回退了,所以向前檢查。

GACGAACGACCGACGACGCCGACGAC

----------------------GACGCCG

i=15,j=4,next[3]=1

GACGAACGACCGACGACGCCGACGAC

----------------------------GACGCCG

回退之後比對成功,i++,j++,重覆這一過程,直到……

 

GACGAACGACCGACGACGCCGACGAC

 

----------------------------GACGCCG

至此我們便利用next數組完成了模式串匹配的過程。

可以看到,next數組使得匹配過程中少了很多不必要的計算,整個匹配過程顯得高效利落。

 

那麼問題來了,怎樣高效地求next數組?暴力是肯定行不通的。

事實上,next數組的求法和上述KMP演算法的步驟驚人地相似,甚至可以說,求next數組的過程就是一個自我匹配的過程。

也就是:求next[j],就是讓pattern[0..j]和pattern[0..x]進行上述的匹配過程。這裡x=next[j-1]。next[j]=能夠匹配成功的子串的最大長度。

下令pattern=”GACCGGACCGA“

首先令next[0]=0,易知next[1]=0。

之後,由於pattern[2]!=pattern[0],所以next[2]=0。同理next[3]=0。

由於pattern[4]==pattern[0],所以next[4]=0+1=1。

這裡的0是next[3]的值。表示字元pattern[4]可以接到next[3]對應的尾碼之後。

由於pattern[5]!=pattern[1],pattern[5]==pattern[next[0]]==pattern[0],所以next[5]=1。

詳情如下:

GACCGG

--------GA    【 用pattern[0..1]匹配pattern[0..5],這裡的1是next[4] 】

匹配不通過,所以令”模式串“的游標移至next[1]=0處(加了引號是因為這裡的”模式串“是對pattern本身進行匹配)

GACCGG

----------GA

類似地,可得:next[6]=next[5]+1=2,next[7]=next[6]+1=3,next[8]=next[7]+1=4,next[9]=next[8]+1=5。

next[10]的求法如下:

GACCGGACCGA

----------GACCGG    【 next[4]=1 】

 

GACCGGACCGA

------------------GACCGG

故next[10]=2。

 

代碼如下:

 1 #include <cstdio>
 2 #include <cstring>
 3 
 4 const int maxL=1000005;
 5 
 6 char obj[maxL];
 7 char pattern[maxL];
 8 
 9 void input()
10 {
11     scanf("%s%s",obj,pattern);
12 }
13 
14 int next[maxL]={0};
15 
16 int getNext()
17 {
18     next[0]=0;
19     int len=strlen(pattern);
20     for(int i=1;i<len;i++)
21     {
22         int k=next[i-1];
23         while(k>=0 && pattern[i] != pattern[k]) k=k?next[k-1]:-1;
24         next[i]=k+1;
25     }
26     return len;
27 }
28 
29 int kmp() //searching for the first place that pattern appears in obj
30 {
31     int lenObj=strlen(obj);
32     int lenPattern=getNext();
33     for(int i=0,j=0;i<lenObj;i++)
34     {
35         while(j>=0 && obj[i] != pattern[j]) j=j?next[j-1]:-1;
36         if(lenPattern==(++j)) return i-j+1;
37     }
38     return -1; //not found
39 }
40 
41 int main()
42 {
43     input();
44     printf("%d\n",kmp());
45     return 0;
46 }
View Code

(我們用-1作為迴圈終止的條件)

附一道簡單的練習題,求pattern在obj中出現的次數。

 1 #include <cstdio>
 2 #include <cstring>
 3 
 4 const int maxL=1000005;
 5 
 6 char obj[maxL];
 7 char pattern[maxL];
 8 
 9 void input()
10 {
11     scanf("%s%s",pattern,obj);
12 }
13 
14 int next[maxL]={0};
15 
16 int getNext()
17 {
18     next[0]=0;
19     int len=strlen(pattern);
20     for(int i=1;i<len;i++)
21     {
22         int k=next[i-1];
23         while(k>=0 && pattern[i] != pattern[k]) k=k?next[k-1]:-1;
24         next[i]=k+1;
25     }
26     return len;
27 }
28 
29 int kmp()
30 {
31     int ans=0;
32     int lenObj=strlen(obj);
33     int lenPattern=getNext();
34     for(int i=0,j=0;i<lenObj;i++)
35     {
36         while(j>=0 && obj[i] != pattern[j]) j=j?next[j-1]:-1;
37         if(lenPattern==(++j)) { ++ans; j=next[j-1]; }
38     }
39     return ans;
40 }
41 
42 int main()
43 {
44     int T; scanf("%d",&T);
45     while(T--)
46     {
47         input();
48         printf("%d\n",kmp());
49     }
50     return 0;
51 }
Problem:POJ P3461

 

演算法這東西,難者不會,會者不難。如果理解時遇到了瓶頸,不妨用筆實驗一下,總結規律,也許就能悟出演算法的思想。

ps.推薦另一篇博文:http://blog.csdn.net/OIljt12138/article/details/51107585,可以作為閱讀本文的對照參考


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 分類:Unity、C#、VS2015 創建日期:2016-04-10 一、簡介 Unity擁有功能完善的地形編輯器,支持以筆刷繪製的方式實時雕刻出山脈、峽谷、平原、高地等地形。Unity地形編輯器同時提供了實時繪製地表材質紋理、樹木種植、大面枳草地佈置等功能。值得—提的是,Unity中的地形編輯器支... ...
  • 軟體下載: http://hovertree.com/h/bjaf/hwqtjwjs.htm 截圖:使用方法:點擊按鈕,選擇文件夾,就可以顯示文件夾中包含的文件總數。這個項目包含在HoverTree解決方案中。源碼下載:http://hovertree.com/h/bjaf/cao15h74.htm ...
  • 一、引言 在前一篇文章中,我向大家介紹瞭如何實現實現端對端聊天的功能的,在這一篇文章中將像大家如何使用SignalR實現群聊這樣的功能。 二、實現思路 要想實現群聊的功能,首先我們需要創建一個房間,然後每個線上用戶可以加入這個房間裡面進行群聊,我們可以為房間設置一個唯一的名字來作為標識。那Signa ...
  • 1.前言 現在這個項目已經有階段性的模塊完成了,所以就想著對這些模塊進行單元測試,以保證項目的代碼的質量。首先雖然標題是mvc+webapi實質上我只是對mvc進行的測試。用的時候vs的unit test generator.至於它的安裝和介紹在這不做詳細介紹。好的現在開始總結我的單元測試總結。 2 ...
  • 前言當我們訪問某個網站的時候需要檢測用戶是否已經登錄(通過Session是否為null),我們知道在WebForm中可以定義一個BasePage類讓他繼承System.Web.UI.Page,重寫它的OnInit()方法,在OnInit()中判斷Session中是否有用戶登錄的信息。 在mvc下該怎 ...
  • 首先是IEnumerable與IEnumerator的定義: 1.IEnumerable介面允許使用foreach迴圈,包含GetEnumerator()方法,可以迭代集合中的項。 2.IEnumerator介面是一個真正的集合訪問器,它包含MoveNext()方法和Current屬性,在forea ...
  • HoverTree解決方案學習C#.NET,Sql Server,WinForm等的解決方案。本文鏈接http://hovertree.com/h/bjaf/0jteg8cv.htm使用的技術、框架、工具包括:.NET Framework 4.5.2Visual Studio 2015Sql Ser ...
  • 對於EF對資料庫的緩存,EF本身也有,但是不能靈活的控制,而且實體對象釋放了緩存就沒有了,總不能使用同一個實體對象(實體對象不支持多線程),基本上就是用完就釋放,而EF的一個擴展框架也提供了緩存操作(源碼:https://github.com/loresoft/EntityFramework.Ext ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...