標籤:
原創作品,轉載請註明出處:點我
假設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 14A = a b a b a b a a b a b a c bB = a b a b a c bj = 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 b0 0 1 2 3 0 01、首字元a,一律設為0,即P[1]=02、“ab” 第一個字元為a,最後一個字元為b,不相等,所以長度為0,即P[2]=03、“aba”,頭兩個字串為“ab”,後兩個為"ba",不相同,頭一個字串為a,後一個也為a,相同,所以長度為1,即P[3]=14、"abab",頭兩個為ab,後兩個為ab,相同,頭三個位aba,末尾三個為bab,不同,所以最大長度為2,即P[4]=25、"ababa",頭三個位aba,末尾三個也為aba,頭四個為abab,末尾四個為baba,不同,所以最大長度為3,即P[5]=3以此類推,可以得出數組P[j] 求出了P[j]之後,就可以根據P[j]進行匹配了,還是以上面的A、B為例,匹配過程中用到的幾個變數pattern表示B,Target表示AheadIndex指向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
下面是原始碼實現
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單個字元,所以最前面和最後面相同的子串的最大長度為a011 //aba,最前面一個a和最後面一個元a素a相同,所以值為a1,abab最前面2個ab和最後面兩個ab相同,值為a212 //ababa最前面3個為aaba,最後面3個為aaba,所以值為a313 void KMP::getNext()14 {15 mVec.clear(); //清空?ec16 //vec.push_back(0);//為a了使用方便,vec的第一個資料不用17 mVec.push_back(0); //第一個字元的下一個位置一定是0,比如"ababacb",首字元a的值為018 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 do25 {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 else75 {76 if(0 == patternIndex)//如果第一個字元就不匹配,則把mTarget左移一位77 ++headIndex;78 else79 {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 }
KMP演算法匹配原理以及C++實現