字符串匹配——KMP算法(leetcode 28)


1.前言

  在一個字符串中尋找是否包含目標字符串,實現這個要求並不難,遍歷文本的每個字符串,如果和目標字符串的第一個匹配,就把匹配的字符后移一位繼續對比,直到不匹配,然后將文本的指針后移一位,繼續對比即可。但是這樣的暴力匹配最壞情況的時間復雜度為O(n*m),而KMP算法可以將其復雜度降低到O(n+m),減少重復對比次數。

 

2.正文

  在學習KMP算法時,我翻閱了不少博客,但是五花八門的KMP介紹讓我有點迷糊,有時候似乎看懂了,但是換一個例子我似乎不明白應該如何構造next數組,直到看了這個視頻,對於KMP不懂的小伙伴們可以看看這個視頻,視頻大概在4-12分鍾中講解了如何構造利用next數組,並如何使用next數組減少重復對比。

  比起那些干澀的博客,這個視頻提供了很好的例子說明如何在O(n)級數下構造next數組(利用已經求得的前面部分next來構造后面的部分),並且提供了幾個例子生動的說明,next數組的作用:記錄當前的后綴字串與前綴子串最大匹配長度。

  在這里不具體說明,有問題的小伙伴們可以去看看上面的視頻,看完之后嘗試自己動手寫一個kmp算法。下面我會貼出看完這個視頻后我自己實現的kmp。

 

3.實現

  以下是KMP算法實現,主要包含kmp函數返回-1(未匹配)或匹配的第一個位置(下標從0開始),本人的next數組下標是從0開始到length-1,還包括一個getNext函數用來構造next數組,沒有考慮text以及pattarn的一些異常輸入,主要是為了更單純的實現kmp算法。

 1     static int kmp(char[] text,char[] pattarn){
 2         int[] next = new int[pattarn.length];
 3         getnext(pattarn,next);
 4         int m = 0;//matchLength
 5         for(int i = 0;i<text.length;i++){
 6             while(m>0&& pattarn[m] != text[i]){
 7                 m = next[m-1];
 8             }
 9             if(text[i] == pattarn[m]){
10                 m++;
11                 if(m == pattarn.length){
12                     return i - m+1;
13                 }
14             }
15         }
16         return -1;
17     }
18 
19     private static void getnext(char[] pattarn, int[] next) {
20         int q = 0;//q代表前一個字符前后綴能匹配的最大長度
21         for(int i = 1;i<pattarn.length;i++){//next[0] = 0,因此從1開始
22             while(q > 0 && pattarn[q] != pattarn[i]){//遞歸直到q為0(沒有匹配的前綴)或者當前字符與q相等時(不斷“遞歸”查前綴匹配的前一個位置q)
23                 q = next[q-1];//如果不相等,如“acad”,i=3,q=1,則q變成next[q-1](q-1是不匹配的前一個位置)
24             }
25             if(pattarn[q] == pattarn[i]){
26                 q++; 
27             }
28             next[i] = q;
29         }
30     }

  對於更完善的kmp以及入參的異常處理實現在下面貼出,也已經通過leetcode 28 Implement strStr()。其實就是對傳入參數進行了一些意外值處理。

 1 class Solution {
 2     public int strStr(String haystack, String needle) {
 3         if (needle.length() == 0)
 4             return 0;
 5         if (haystack.length() < needle.length())
 6             return -1;
 7         
 8         char[] text = haystack.toCharArray();
 9         char[] pattarn = needle.toCharArray();
10         int[] next = new int[pattarn.length];
11         getnext(pattarn,next);
12         int m = 0;//matchLength
13         for(int i = 0;i<text.length;i++){
14             while(m>0&& pattarn[m] != text[i]){
15                 m = next[m-1];
16             }
17             if(text[i] == pattarn[m]){
18                 m++;
19                 if(m == pattarn.length){
20                     return i - m+1;
21                 }
22             }
23         }
24         return -1;
25     }
26     
27     private static void getnext(char[] pattarn, int[] next) {
28         int q = 0;//q代表前一個字符前后綴能匹配的最大長度
29         for(int i = 1;i<pattarn.length;i++){//next[0] = 0,因此從1開始
30             while(q > 0 && pattarn[q] != pattarn[i]){//遞歸直到q為0(沒有匹配的前綴)或者當前字符與q相等時(不斷“遞歸”查前綴匹配的前一個位置q)
31                 q = next[q-1];//如果不相等,如“acad”,i=3,q=1,則q變成next[q-1](q-1是不匹配的前一個位置)
32             }
33             if(pattarn[q] == pattarn[i]){
34                 q++; 
35             }
36             next[i] = q;
37         }
38     }
39 }

 

  在理解了KMP的流程后,趕緊動手實現一下吧。實現完還可以去leetcode測試一下哦!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM