KMP 字符串的匹配算法

来源:互联网 发布:h3c端口聚合配置验证 编辑:程序博客网 时间:2024/06/05 22:37

一、KMP算法与Brute-force算法的最大区别就在于KMP算法在比较时,主串的指针i不用回溯,只需回溯字串的指针j

而Brute-force算法就是简单的从第一个字符比到最后一位,这时需要对主串的i进行回溯,这样算法的比较次数明显增多

KMP的算法复杂度可为O(m +n)而Brute-force的算法复杂度可为O(m*n)。

二、在做KMP时我们可以知道每一次字串j的回溯,即每一次字串后移的位数等于匹配的位数减去部分匹配的位数

即可以得到一公式   移位数 = 匹配位数 - 部分匹配位数

如何得到部分匹配位数呢?

这是由字串的前缀与后缀的最大共同的字符串决定的。如ABDAB这是部分匹配位数为2

三、如何推算next[]呢?

由特殊到一般:

    假设:有next[j]= k,即“t[0]...............t[k-1]”=''t[j-k]................t[j-1]'',0<k<j,此时next[j+1]有两种情况

          字串:

             t = t[0].........t[j-k]......t[j-1].......t[j]......t[m-1]

                           t' = t[0].......t[k-1]      t[k]

                              t' =t[0]....................t[k']

if(t[k] == t[j]) {         next[j+1] = next[j] +1 = next[k] +1;}//此时我们就需从字串t'中找到t' 等于t[k]的情况else if(t[k ] != t[j]){         next[j+1]  = k'+1= next[k]+1;}//如果没找到则k = 0else{          next[j+1] = 0;}

//以上代码只是分析使用的是伪码。

KMP代码:

/**
*KMP 字符串匹配算法核心是GetNext[]这一步,要知道在j>1时不匹配应该保证i不变,获取next[j]的值
*由字符串的比较我们可以知道我们可以保证i不变来做比较,i是不用回溯的只需回溯j的值就 可以了
*/

#include <iostream>#include <windows.h>#include <string.h>using namespace std;typedef struct{      char str[100];  int length ;}KMPString;


/**
*字符串的比较
*@param S,start ,T,next分别表示主串,开始比较的位置,字串以及next[j]的值
*@return 返回成功匹配后的v值表示成功的第一次出现的下标,如果匹配失败返回-1; 
*/
int KMPIndex(KMPString S,int start,KMPString T,int next[]){cout<<"字串:"<<endl;for(int i = 0;i <T.length;i++){      cout<<T.str[i];}cout<<endl;cout<<"主串:"<<endl;for(int i = 0;i <S.length;i++){      cout<<S.str[i];}cout<<endl;  int i = start;  int j = 0;  int count = 0;  int num = 0;  int v ;  while(i <S.length && j <T.length){      if(S.str[i] == T.str[j]){    count++;        i++;    j++;  }  else if(j == 0){  i++;  }else{    num++;        j = next[j];  }  }  if(j == T.length){  cout<<"匹配成功!"<<endl;         v = i-T.length;  }else{  cout<<"匹配失败!"<<endl;         v = -1;  }   cout <<"count="<<count<<" "<<"num = " <<num<<endl;      return v;         }


//获取next[j]
void GetNext(KMPString T,int next[]){//  cout<<T<<endl;    int j = 1;int k = 0;next[0] = 0;next[1] = 0;cout<<"T长度:"<<T.length <<endl;for(int i = 0;i <T.length;i++){      cout<<T.str[i]<<endl;}while(j < T.length){cout<<"第"<<j<<"次进入while循环"<<endl;if(T.str[j] == T.str[k]){     next[j+1] = k+1; j++; k++;}else if(k == 0){//这里是个出口,在循环后没有找到真串时,下一步跳回第一步也就是j 回溯到0 cout<<"进入k ==0"<<endl;     next[j+1] = 0; j++;}else{//这里可以不断的获取到真串的存在,如果存在next[j]下一步调到j = kcout<<" k = next[k]"<<endl;    k = next[k];}}for(int i = 0;i <T.length;i++){      cout<<"next["<<i<<"]="<<next[i]<<endl;}      cout<<"k="<<k<<endl;}
//main函数

int main(){KMPString  S = {{"aaaaaaaab"},9},T = {{"aaaab"},5};//KMPString  S = {{"aaaaaaaa"},8},T = {{"aaaab"},5};int next[100] ={0,1,2,3};int count = 0 ;int pos =0;cout<<"获取GetNext[j]"<<endl;GetNext(T,next);cout<<"获取next成功!"<<endl;pos = KMPIndex(S,0,T,next);if(pos >= 0){       cout<<"获取到匹配成功的位置!"<<endl;           cout<<"匹配成功的位置:"<<pos<<endl;   for(int i = pos ;i <S.length;i++){         cout<<S.str[i];   }   cout<<endl;}  system("PAUSE");      return 0;}


3 0