kmp算法

来源:互联网 发布:同花顺软件时间不对 编辑:程序博客网 时间:2024/06/06 22:35

先看下代码

[cpp] view plain copy
  1. int index(string s,string t,int pos)  
  2. {  
  3.       
  4.     for (int i=pos;i<s.length()&&i+t.length()<=s.length();i++){  
  5.         int flag=1;  
  6.         for (int j=0;j<t.length();j++){  
  7.             if (s[i+j]!=t[j]){  
  8.                 flag=0;  
  9.                 break;  
  10.             }  
  11.         }  
  12.         if (flag){  
  13.             return i+1;  
  14.         }  
  15.     }  
  16.     return 0;  
  17. }  

返回的是b串第一次在a串的pos位置后出现的位置

可以显而意见的是复杂度为O(n*m)

在a串和b串为如下的形式的时候时

a:0000000001

b:001

你会发现b串在a串中进行匹配的时候,要到最后一次才会匹配成功,而前面的匹配很多都做了无用功,i指针根本就没必要回朔


kmp算法

kmp算法巧妙的解决了这个问题

将问题的复杂度下降到O(n+m)

那么它是怎么实现的呢?

先给大家看几个图示


当a串与b串进行比较的时候,可能在分别在i和j的位置的时候匹配失败了

这个时候如果是一般匹配方法就会i指针回朔到b1+1位置,而j则会回朔到b2位置

但是如果仔细观察下你会发现(b1,i-1)和(b2,j-1)是完全匹配的,而且你也可能会发现可能在b串中本身就存在(b2,s)完全等于(k,j-1)的情况,那么这个时候何不直接把j回朔到s+1的位置呢?

如下图所示



这样算法复杂度就可以得到很好的优化

但是如何实现以上过程呢

这里引进next数组

next[i]:=如果匹配失败了j指针应该滑向的位置

也可以理解成最大的一个值使得对于b串而言(0,next[i]-1)完全等于(j-next[i],j-1)

则可以定义如下

next[i]=-1,i==0

next[i]=k,,0<k<j,其中"p0p1p2p3...pk-1"="pj-kpj-k+1pj-k+2...pj-1"

next[i]=0,其余的情况,其实也就是b串0到j-1的位置上没有前后相互匹配的情况


那么只要求出next数组的值,就万事大吉了

下面给出代码

[cpp] view plain copy
  1. void getNext(char *p,int *next)  
  2. {  
  3.     int j,k;  
  4.     next[0]=-1;  
  5.     j=0;  
  6.     k=-1;  
  7.     while(j<strlen(p)-1)  
  8.     {  
  9.         if(k==-1||p[j]==p[k])    //匹配的情况下,p[j]==p[k]  
  10.         {  
  11.             j++;  
  12.             k++;  
  13.             next[j]=k;  
  14.         }  
  15.         else                   //p[j]!=p[k]  
  16.             k=next[k];  
  17.     }  
  18. }  
  19. int KMPMatch(char *s,char *p)    //如果不存在匹配项,则返回-1,否在返回开始的匹配位置  
  20. {                                //p匹配到s,s必须比p长  
  21.     int next[100];  
  22.     int i,j;  
  23.     i=0;  
  24.     j=0;  
  25.     getNext(p,next);  
  26.     while(i<strlen(s))  
  27.     {  
  28.         if(j==-1||s[i]==p[j])  
  29.         {  
  30.             i++;  
  31.             j++;  
  32.         }  
  33.         else  
  34.         {  
  35.             j=next[j];       //消除了指针i的回溯  
  36.         }  
  37.         if(j==strlen(p))  
  38.             return i-strlen(p);  
  39.     }  
  40.     return -1;  
  41. }  

写法二,思路都是一样的,只是代码习惯的不同。。

[cpp] view plain copy
  1. #include <bits/stdc++.h>  
  2. using namespace std;  
  3. #define maxn 105  
  4. int next[maxn];  
  5.   
  6. void get_next(string b)  
  7. {  
  8.     next[0]=-1;  
  9.     int j=0;  
  10.     for (int i=1;i<b.length();i++){  
  11.         while(j>0&&b[i]!=b[j]) j=next[j];  
  12.         if (b[i]==b[j]) next[i]=j++;  
  13.     }  
  14. }  
  15.   
  16. int kmp(string a,string b,int pos)  
  17. {  
  18.     memset(next,0,sizeof(next));  
  19.     get_next(b);  
  20.       
  21.     int j=0;  
  22.     for (int i=pos;i<a.length();i++){  
  23.         while(j>0&&a[i]!=b[j]) j=next[j];  
  24.         if (a[i]==b[j]) j++;  
  25.         if (j==b.length()){  
  26.             return i-b.length()+1;  
  27.         }  
  28.     }  
  29.     return -1;  
  30. }  
  31.   
  32. int main()  
  33. {  
  34.     string a,b;  
  35.     while(cin>>a>>b){  
  36.         printf("%d\n",kmp(a,b,0));  
  37.     }  
  38.     return 0;  
  39. }  
1 0