KMP算法匹配原理以及C++實現

原創做品,轉載請註明出處: 點我
假設A表示目標字符串,A="abababaababacb",B表示匹配模式,B="ababacb"
用兩個指針i和j分別表示,A[i-j+1 .... i]與B[1...j]徹底相等。也就是說,i是不斷增長的,隨着i的增長j相應的變化,且知足以A[i]結尾的長度爲j的字符串正好匹配B串的前j個字符(j固然越大越好),如今須要jianyanA[i+1]和B[j+1]的關係。當A[i+1]=B[j+1]時,i和j各自增長一,何時j=m了,咱們就說B是A的子串(B串已經完整了),而且跟根據這使得i值算出匹配的位置。當A[i+1]<>B[j+1],KMP的策略是調整j的位置(減少j值)使得A[i-j+1...i]與B[1...j]保持匹配且新的B[j+1]剛好與A[i+1]匹配。
i  = 1  2  3  4  5   6   7  8  9 10 11 12 13  14
A = a  b  a  b  a   b  a  a  b  a   b   a   c   b
B = a  b  a  b  a   c  b
j  = 1  2  3  4  5   6  7
當i,j等於5時,A[i+1]跟B[j+1]不相等,這是要縮小j爲j'(也就是要把B字符串往右移)。咱們發現,j'必需要使得B[1...j]中的頭j'個字母和末j'個字母徹底相等,這樣j變成j'後才能繼續保持i和j的性質。固然,j'越大越好。小心的j爲3時,剛好符合要求。
咱們能夠知道,新的j能夠取多少跟i無關,只與B串有關。咱們能夠預處理出這樣的一個數組P[j],表示當匹配到B數組的第j個字母而第j+1個字母不能匹配的時候,心的j的最大值是多少。
以B="ababacb"爲例,解釋P[j]數組的求結果
a b a b a c b
0 0 1 2 3 0 0
一、首字符a,一概設爲0,即P[1]=0
二、「ab」 第一個字符爲a,最後一個字符爲b,不相等,因此長度爲0,即P[2]=0
三、「aba」,頭兩個字符串爲「ab」,後兩個爲"ba",不相同,頭一個字符串爲a,後一個也爲a,相同,因此長度爲1,即P[3]=1
四、"abab",頭兩個爲ab,後兩個爲ab,相同,頭三個位aba,末尾三個爲bab,不一樣,因此最大長度爲2,即P[4]=2
五、"ababa",頭三個位aba,末尾三個也爲aba,頭四個爲abab,末尾四個爲baba,不一樣,因此最大長度爲3,即P[5]=3
以此類推,能夠得出數組P[j]  
求出了P[j]以後,就能夠根據P[j]進行匹配了,仍是以上面的A、B爲例,匹配過程當中用到的幾個變量
pattern表示B,Target表示A
headIndex指向A中跟B進行匹配的子串的首字符
targetIndex指向A中正在跟B匹配的字符的索引,patternIndex指向B中正在匹配的字符在B中的索引
targetIndex等於向右移動的位數加上patternIndex,即targetIndex=headIndex-1+patternIndex
第一步、
此時,patternIndex= 1,targetIndex= 1,headIndex=1
此時pattern[patternIndex] == target[targetIndex],而後patternIndex跟targetIndex增長一,再接着比較是否相同
直到targetIndex跟patternIndex爲6的時候,pattern[patternIndex] != target[targetIndex]
此時,就須要把B向右移動,進行下一次的匹配,那移動多少比較好呢?這就須要根據P[j]來計算
因爲此時, patternIndex前面的ababa已經匹配了,P[5]=3,前面匹配的字符串ababa的長度爲5,因此字符串pattern向右移動的位數爲5-3=2,即pattern向右移動到3,即新的headIndex=headIndex+2=3;而新的patternIndex=P[5]+1=4,即新的patternIndex指向B串中的第四位,targetIndex=headIndex+patternIndex-1=3+4-1=6,因此移動以後的狀況以下圖
此時,此時pattern[patternIndex] == target[targetIndex],而後patternIndex跟targetIndex增長一,再接着比較是否相同
當patternIndex等於6,targetIndex等於8時,pattern[patternIndex] != target[targetIndex],又要把B串往右移,此時,
P[5]=3,前面的ababa已經匹配,長度爲5,因此向右移動的位數爲5-3=2,此時,headIndex=headIndex+2=3+2=5,patternIndex=P[5]+1=4,指向B串中的第四位,targetIndex=headIndex+patternIndex-1=5+4-1=8,因此targetIndex指向A串中的第八位
此時pattern[patternIndex] != target[targetIndex],又要把B串往右移,此時前面的aba已經匹配成功,長度爲3,P[3]=1,因此往右移動的長度爲3-1=2,移動兩位,此時,headIndex=headIndex+2=5+2=7,patternIndex=P[3]+1=1+1=2指向B串中的第二位,targetIndex=headIndex+patternIndex-1=7+2-1=8,指向A串的第八位
此時pattern[patternIndex] != target[targetIndex],又要把B串往右移,此時前面已經匹配的串爲a,長度爲1,P[1]=0,往右移動的位數爲1-P[1]=1-0=1;
此時,headIndex=headIndex+1=7+1=8,patternIndex=P[1]+1=1,指向B串的第一位,targetIndex=headIndex+patternIndex-1=8+1-1=8,指向A串的第八位,如圖所示
此時再一次匹配,就會匹配成功。
下面是KMP算法的C++實現,有點小問題
 1 #ifndef __KMP__H__
 2 #define __KMP__H__
 3 #include <string>
 4 #include <vector>
 5 using namespace std;
 6 
 7 class KMP{
 8 public:
 9              //void static getNext(const string &str,vector<int> &vec);
10              int kmp();
11             KMP(){}
12             KMP( const string &target,const string &pattern):mTarget(target),mPattern(pattern){}
13             void setTarget(const string &target);
14             void setPattern(const string &pattern);
15 private:
16             vector< int> mVec;
17             string mTarget;
18             string mPattern;
19             void getNext();
20 };
21 #endif

下面是源代碼實現html

 1 #include "KMP.h"
 2 #include <iostream>
 3 #include <vector>
 4 using namespace std;
 5 
 6 
 7 //獲取字符串str的全部子串中相同子集的長度
 8 //好比字符串ababacb,分別獲取字符串a,ab,aba,abab,ababa,ababac,ababacb中D
 9 //最前面和最後面相同的子串的最大長度,好比
10 //a:由於aa爲a單個字符,因此最前面和最後面相同的子串的最大長度爲a0
11 //aba,最前面一個a和最後面一個元a素a相同,因此值爲a1,abab最前面2個ab和最後面兩個ab相同,值爲a2
12 //ababa最前面3個爲aaba,最後面3個爲aaba,因此值爲a3
13 void KMP::getNext()
14 {
15       mVec.clear(); //清空?ec
16       //vec.push_back(0);//爲a了使用方便,vec的第一個數據不用
17       mVec.push_back(0); //第一個字符的下一個位置必定是0,好比"ababacb",首字符a的值爲0
18       string::const_iterator start = mPattern.begin();
19       string::const_iterator pos = start + 1;
20       while(pos != mPattern.end())
21       {
22             string subStr(start,pos+1); //獲取子字符串
23             int strLen = subStr.size() - 1;//獲取子串中D先後相同的子子串的最大長度
24             do
25             {
26                    string prefix(subStr,0,strLen); //獲取subStr中D的前面strLen子集
27                    string postfix(subStr,subStr.size()-strLen,strLen); //獲取subStr中D的前面?trLen子集
28                    if(prefix == postfix)
29                    {
30                            mVec.push_back(strLen);
31                            break;
32                     }
33                     --strLen;
34                     /若是先後相同的子集的長度小於一
35                     /說明沒有相同的,則把0壓棧
36                     if(strLen < 1)
37                        mVec.push_back(0);
38              } while(strLen > 0);
39 
40              ++pos;
41        }
42 }
43 
44 void KMP::setPattern(const string &pattern)
45 {
46       mPattern = pattern;
47 }
48 
49 void KMP::setTarget(const string &target)
50 {
51       mTarget = target;
52 }
53 
54 
55 
56 
57 int KMP::kmp()
58 {
59      getNext(); //首先獲取next數據
60      int targetIndex = 0;
61      int patternIndex = 0;
62      int headIndex = 0;//指向跟pattern匹配的Target的第一個元素的索引
63      while(patternIndex != mPattern.size() && targetIndex != mTarget.size())
64      {
65            for(int i = 0; i < mPattern.size()-1;++i)
66            {
67                   if(mPattern[patternIndex] == mTarget[targetIndex])
68                   {
69                          ++patternIndex;
70                          ++targetIndex;
71                          if(mPattern.size()== patternIndex)//若是已經匹配成功,則退出循環
72                                  break;
73                    }
74                    else
75                    {
76                          if(0 == patternIndex)//若是第一個字符就不匹配,則把mTarget左移一位
77                                   ++headIndex;
78                          else
79                          {
80                                   headIndex += patternIndex - mVec[patternIndex-1];//因爲vector索引從零開始,因此要減去一
81                                   patternIndex = mVec[patternIndex-1];//更新patternIndex索引
82                          }
83                          targetIndex = headIndex + patternIndex;//跟新targetIndex索引
84                          break;
85                     }
86 
87             }
88       }
89 
90       return headIndex;
91 }
相關文章
相關標籤/搜索