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

查找子字符串----KMP算法深入剖析的更多相关文章

  1. 数据结构与算法--KMP算法查找子字符串

    数据结构与算法--KMP算法查找子字符串 部分内容和图片来自这三篇文章: 这篇文章.这篇文章.还有这篇他们写得非常棒.结合他们的解释和自己的理解,完成了本文. 上一节介绍了暴力法查找子字符串,同时也发 ...

  2. C 查找子字符串

    自己用 C 写的一个查找子字符串的函数 int findstr(char *str,char *substr) //C实现 find{ if(NULL == str || NULL== substr) ...

  3. 子字符串查找之————关于KMP算法你不知道的事

    写在前面: (阅读本文前需要了解KMP算法的基本思路.另外,本着大道至简的思想,本文的所有例子都会做从头到尾的讲解) 作者翻阅了大量网上现有的KMP算法博客,发现广为流传的竟然是一种不完整的KMP算法 ...

  4. 数据结构(复习)---------字符串-----KMP算法(转载)

    字符串匹配是计算机的基本任务之一. 举例来说,有一个字符串"BBC ABCDAB ABCDABCDABDE",我想知道,里面是否包含另一个字符串"ABCDABD" ...

  5. [Swift]扩展String类:实现find()查找子字符串在父字符串中的位置

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

  6. 字符串 kmp算法 codeforce 625B 题解(模板)

    题解:kmp算法 代码: #include <iostream>#include <algorithm>#include <cstring>#include < ...

  7. 模板 - 字符串 - KMP算法

    要先理解前缀函数的定义,前缀函数 \(\pi(i)\) 表示字符串 \(s[0,i]\) 的同时是其最长真前缀及最长真后缀的长度,简单来说就是这个 \(s[0,i]\) 首尾最长的重叠长度(不能完全重 ...

  8. 字符串KMP算法

    讲解:http://blog.csdn.net/starstar1992/article/details/54913261 #include <bits/stdc++.h> using n ...

  9. 二十六、JavaScript之查找子字符串substring和slice和substr

    一.代码如下 二.效果如下 <!DOCTYPE html> <html> <meta http-equiv="Content-Type" conten ...

随机推荐

  1. Nested weights are bad for performance

    警告信息“Nested weights are bad for performance”的消除方法 原因分析:在布局进行嵌套使用时,父布局与子布局都使用了android:layout_weight,但 ...

  2. Qt5:QSystemTrayIcon类实现程序托盘图标

    windows下,在许多应用程序中都会实现一个托盘图标,用于隐藏应用程序窗口时还能对该应用程序进行简单的操作,例如 QQ ,renren等程序 那么,在Qt中,如何实现呢? 这就要用到Qt提供的 QS ...

  3. ubuntu安装mysql数据库

    http://www.cnblogs.com/zhuyp1015/p/3561470.html http://www.2cto.com/database/201401/273423.html http ...

  4. Flocker 做为后端存储代理 docker volume-driver 支持

    docker Flocker https://github.com/ClusterHQ/flocker/ 文档: https://docs.clusterhq.com/en/latest/docker ...

  5. 要你的祝福.lrc

    要你的祝福(电影<我是路人甲>插曲 试听版) - 李潇潇 午夜的温度慢慢起舞 穿梭的人潮有些荒芜 开始欢呼 开始麻木 谁被谁在安抚 落单的幸福变得模糊 孤单的城市独自起舞 也许满足 也许糊 ...

  6. Delphi XE7,Rad Studio XE7 官方下载(附Delphi XE7破解),更新Update1(转)

    源:http://blog.csdn.net/maxwoods/article/details/39024525

  7. swift之向ftp服务器传文件

    在 mac 上如何使用 xcode, swift 语言开发一个向 ftp 服务器上传文件的工具? 使用的是第三方库 Rebekka,下载地址为:https://github.com/Constanti ...

  8. rsync+inotify实现数据的实时备份

    一.rsync概述 1.1.rsync的优点与不足 rsync与传统的cp.tar备份方式相比,rsync具有安全性高.备份迅速.支持增量备份等优点,通过rsync可以解决对实时性要求不高的数据备份需 ...

  9. [转] M2E插件maven-dependency-plugin问题

    转自 : http://blog.csdn.net/cskgnt/article/details/8530526 问题: maven-dependency-plugin (goals "co ...

  10. 编写一个python脚本功能-备份

    版本一 解决方案当我们基本完成程序的设计,我们就可以编写代码了,它是对我们的解决方案的实施.版本一例10.1 备份脚本——版本一 #!/usr/bin/python # Filename: backu ...