查找子字符串----KMP算法深入剖析



原文: http://blog.csdn.net/dazhong159/article/details/7971438 

笔记:

KMP算法思想

L(I, j)为以s1[i], s2[j]结尾的最大子串的长度, L(I+1, j+1)=s1[I+1]==s2[j+1]?L(I,j)+1:0



假设主串:a b a b c a b c a c b a b
        子串:a b c a c

1、一般匹配算法

逐个字符的比较,匹配过程如下:
  第一趟匹配
  a b a b c a b c a c b a b
  a b c
  第二趟
  a b a b c a b c a c b a b
     a
  第三趟
  a b a b c a b c a c b a b
      a b c a c
  第四趟
  a b a b c a b c a c b a b
        a
  第五趟
  a b a b c a b c a c b a b
           a
  第六趟
  a b a b c a b c a c b a b
            a b c a c

匹配成功。

性能分析:情况好:时间复杂度O(m+n);情况差:时间复杂度O(m*n)。  

2、一般匹配算法改进

  即KMP算法。可以发现上面的算法,每一趟匹配过程中出现字符不等时,回溯指针,如果将其改进,指针不回溯,利用已经得到的部分匹配的结果将模式向右移动的更远一些,然后继续比较。那么算法性能会得到大大的提高。
  看到上面的过程,在第三趟的匹配过程中,当i=6,j=4字符不等时,又从i=3,j=0重新开始比较。其实可以容易发现,在i=3和 j=0,i=4和i=0以及i=5和j=0这3次比较都是不必进行的。因为从第三趟部分匹配结果就可以得出,主串中第3,4,5个字符是’b’,’c’,’a’。而模式中第一个字符是’a’,因此无需和这3个字符进行比较了,紧需要向右移动3个字符继续进行i=6,j=1时字符串比较就行了。那么一种理想的模式匹配就可以的出来了。

KMP匹配过程如下:
  第一趟
  a b a b c a b c a c b a b
  a b c
  第二趟
  a b a b c a b c a c b a b
      a b c a c
  第三趟
  a b a b c a b c a c b a b
            a b c a c
匹配成功,可以看出算法效率提高了不少。

3、剖析KMP算法:

假设(n>m)
  主串:s0 s1 s2 s3 s4 s5 s6 …… s(n)
  模式:p0 p1 p2 p3 p4……….p(m)     
当匹配过程中产生失配(s(i)!=p(j))时,主串的第i个字符应与模式中的哪个字符相比较?假设此时与模式中的第k(k<j)个字符相比较,那么就有p0p1…p(k-1)=s(i-k)s(i-k+1)…s(i-1) --式1(就好像上面中绿的的字符a,这里是从模式中第1个字符开始比较与主串中字符a相同)。
  当匹配失配时(s(i)!=p(j)),可以得到p0p1p2p3…p(j-1)=s(i-j)s(i-j+1)…s(i-1) --式2
  从式2可以得到p(j-k)p(j-k+1)…p(j-1)=s(i-k)s(i-k+1)..s(i-1) --式3
  由式1和式3可以得到p0p1…p(k-1)=p(j-k)p(j-k+1)…p(j-1) --式4
  若令next[j]=k,则next[j]表明当模式中第j个字符与主串中相应字符失配时,在模式中需要重新和主串中该字符进行比较的字符位置。那么next 函数定义为:
                     (1)-1 当j=0时
  next[j]= (2)max{k|0<k<j 且式4成立}
                     (3)0  其他情况
那么此时next值如何求得呢?

     由定义知道next[0]=-1;设next[j]=k,这表明在模式串中有这样关系p0p1…p(k-1)=p(j-k)p(j-k+1)…p(j-1) (0<k<j) --式5。此时next[j+1]的值有两中情况:
   (1)若p(k)=p(j), 则:p0p1…p(k)=p(j-k)p(j-k+1)…p(j) --式6,即next[j+1]=k+1。
   (2)若p(k)!=p(j),则:p0p1…p(k)!=p(j-k)p(j-k+1)…p(j)--式7,此时可以把该问题看成模式匹配的问题,整个模式串既是主串又是模式串,这里应将模式向右移动next[k](模式中第k个字符与主串失配时,需要移动的位置)位置,和主串中的第j个字符相比较。若next[k]=k’,且p(j)=p(k’),则可以得到next[j+1]=next[k]+1即 next[j+1]=next[next[j]]+1。那么还要注意下当模式中上一个字符串与下一个字符串相等时候,它们next值是相等的。

4、KMP算法代码:

  1. #include "stdafx.h"  
  2. #include "iostream.h"  
  3. #include "string.h"  
  4.   
  5. //next数组  
  6. void GetNext(char *subStr,int *next)  
  7. {  
  8.     int len=strlen(subStr);  
  9.     next[0]=-1;  
  10.     int i=0,j=-1;  
  11.     while(i<len)  
  12.     {         
  13.         if(j==-1||subStr[i]==subStr[j])  
  14.         {  
  15.             i++;  
  16.             j++;  
  17.             //前后缀字符相等  
  18.             if(subStr[i]==subStr[j])  
  19.                 next[i]=next[j];  
  20.             else  
  21.                 next[i]=j;  
  22.         }  
  23.         else  
  24.             j=next[j];  
  25.     }  
  26. }  
  27.   
  28. //KMP算法  
  29. int KMP(char *str,char *subStr)  
  30. {  
  31.     int lenStr=strlen(str);  
  32.     int lenSubstr=strlen(subStr);  
  33.     int i=0,j=0;  
  34.     int *next=new int[lenStr];  
  35.     GetNext(subStr,next);  
  36.     //遍历主串和子串  
  37.     while(i<lenStr&&j<lenSubstr)  
  38.     {  
  39.         //与一般匹配算法增加了j==-1判断  
  40.         if(j==-1||str[i]==subStr[j])   
  41.         {  
  42.             i++;  
  43.             j++;  
  44.         }  
  45.         //j回溯,i不变  
  46.         else  
  47.             j=next[j];   
  48.     }  
  49.     delete[] next;  
  50.   
  51.     //返回子串的位置  
  52.     if(j>=lenSubstr)  
  53.         return i-lenSubstr;  
  54.     else  
  55.         return -1;  
  56. }  
  57.   
  58. int main()  
  59. {  
  60.     char *str="iloveyouoooyouloveme";  
  61.     char *subStr1="youoooyou";  
  62.     char *subStr2="youoooyou2";  
  63.     cout<<KMP(str,subStr1)<<endl;  
  64.     cout<<KMP(str,subStr2)<<endl;  
  65.     return 0;  
  66. }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值