议题:KMP算法(D.E. Knuth, J.H. Morris, V.R. Pratt Algorithm)

分析:

  • KMP算法用于在一个主串中找出特定的字符或者模式串。现在假设主串为长度n的数组T[1,n],模式串为长度m的数组P[1,m];数组T和P满足:n>m,且所有元素都来自有限字母表中的字符;

  • 常规比较方式是将模式字符串作为滑动窗口从左向右匹配主串的每一个位置,每到一个位置的时候都从当前的第一个字符开始比较,相同则比较下一个字符,否则移 到下一个位置。下左图中顶端字母行表示主串,模式串为nano;可以发现此方式在index=2,index=6和index=10的时候进行了无意义的 比较,其原因在于没有利用模式串本身的性质,比如在index2=2的时候已经发现index=3的位置是a,其肯定不会跟模式串的首位字符匹配,所以可 以直接跳到index=4;KMP算法正是利用模式串本身的信息在一次匹配失败之后,一次跳过后面几个不可能匹配的位置,而不是仅跳回到本次匹配起始位置 的下一个位置。

     
  • 覆盖函数(Overlay Function):为了更好的表述模式串的字符信息,KMP引入了覆盖函数,用于计算字符串的左右自我覆盖程度;上面右图表示字符串abaabcaba的自我覆盖函数结果,0表示有一个覆盖。自我覆盖的数学定义如下:

    对于字符串:a0a1…aj-1aj

    其自我覆盖OF(j)=k定义为:a0a1…ak-1ak = aj-kaj-k+1…aj-1aj (0<=j<=Pattern_len)

    从 覆盖函数的定义中可知,如果某次匹配成功匹配到模式串的第k个字符,则通过OF(k)可以得知模式串末尾端与起始端的覆盖长度,而之前的位置都不可能再次 匹配,所以滑动窗口可以一次移动(Pattern_len-OF(j))长度的位置,并从模式串的OF(j)+1个字符开始比较(之前的字符已经由覆盖函 数确定匹配);同时需要注意,k值需要尽量大,这样才不会将某些匹配丢失。OF(j)表示长度为j的字符串中左端和右端可以匹配的字符的个 数,0<=j<=Pattern_len;

  • 使用递归方式计算模式串中子串对应的OF(k)值:如果已经知道模式串前j个字符的的OF(j)=k,则对于模式串的前j+1个字符的分析如下:

    如果pattern[k+1]==pattern[j+1],则自我覆盖可以往右延长:OF(j+1)=OF(j)+1=k+1

    如果pattern[k+1]!=pattern[j+1],则自我覆盖需要往左缩减:此时的前提条件变成已知前k个字符的OF(k)=h:

    如果pattern[h+1]==pattern[j+1],则OF(j+1)=OF(k)+1=h+1

    如果pattern[h+1]!=pattern[j+1],则重复求OF(h)的过程直到最左边的字符

  • KMP算法中当在主串的m位置作为匹配起始位置,并且当在模式串的j长度时发生匹配失败,下一次在主串中的起始匹配位置不用再回到m+1的位置,而是一次性跳到m+(j-overlay_func[j])的位置;

  • 时间复杂度为O(M+N),其他字符串匹配算法还有如BM(Boyer-Moore)算法和Horspool算法,BM算法的改进算法是SUNDAY- Boyer-Moore-Horspool-Sunday算法;尽管KMP和BM都为线性时间,但是BM算法还是比KMP算法快3-5倍,最快的字符匹配 算法是SUNDAY算法(每次匹配失败后移动的距离更大);

样例:

  1. int* ComputeOverlay(const char *pattern, int pat_len) {
  2.  
  3. /**
  4. * overlay_func数组存储不同长度子数组的自我覆盖度
  5. * 子数组都是以最左边的字符作为开始,一次向右增加一个
  6. * 字符得到的字符串
  7. * */
  8. int *overlay_func=new int[pat_len];
  9. int index;
  10. /**
  11. * 设定单独字符或者没有覆盖的字符串的覆盖度为-1
  12. * */
  13. overlay_func[]=-;
  14.  
  15. for(int i=;i<pat_len;i++) {
  16.  
  17. /**
  18. * 当前长度i的字符串的自我覆盖度由长度为i-1的字符串
  19. * 确定
  20. * */
  21. index=overlay_func[i-];
  22.  
  23. /**
  24. * pattern[i]是新加入的字符,如果其不等于pattern[index+1]
  25. * 则说明:
  26. * &&&&-------&&&&+
  27. * &&&&表示i-1长度字符串中左右端覆盖的字符,如果-与+不等,则说明
  28. * 继续向右移动index也没用,只能在&&&&内部寻找更短的自我覆盖,由于
  29. * i-1长度的左右端已经自我覆盖,所以overlay_func[index]可以找出
  30. * 左/右端内部的覆盖
  31. * */
  32. while(index>= && pattern[i]!=pattern[index+])
  33. index=overlay_func[index];
  34.  
  35. if(pattern[i]==pattern[index+])
  36. overlay_func[i]=index+;
  37. else
  38. /**
  39. * 如果index小于0并且i和index+1位置的字符不等,则说
  40. * 明自我覆盖度为-1
  41. * */
  42. overlay_func[i]=-;
  43.  
  44. }
  45.  
  46. for(int i=;i<pat_len;i++)
  47. printf("%d, ",overlay_func[i]);
  48. return overlay_func;
  49. }
  50.  
  51. int kmp_func(const char *target, int tar_len, const char *pattern, int pat_len) {
  52.  
  53. /**
  54. * 首先针对pattern字符串进行自我覆盖度的计算,once for all。
  55. * */
  56. int *overlay_func=ComputeOverlay(pattern, pat_len);
  57.  
  58. int pat_index=;
  59. int tar_index=;
  60.  
  61. /**
  62. * 从左向右遍历target
  63. * */
  64. while(pat_index<pat_len && tar_index<tar_len) {
  65. if(target[tar_index]==pattern[pat_index]) {
  66. /**
  67. * 如果target和pattern上对应位置的字符相等
  68. * 则两个索引都向右移动一位
  69. * */
  70. tar_index++;
  71. pat_index++;
  72. } else if(pat_index==)
  73. /**
  74. * 如果pattern的索引在第一位,说明第一个字符就
  75. * 不等,所以直接向右移动target上的索引
  76. * */
  77. tar_index++;
  78. else
  79. /**
  80. * 如果pattern的索引不在第一位,说明pat_index-1
  81. * 索引位置之前的字符匹配,则利用KMP的规则,不用将
  82. * tar_index回移,而是直接将pat_index进行移动,其
  83. * 的滑动窗口直接移动在其右端的自我覆盖部分,从而避免
  84. * 中间不必要的匹配循环
  85. * */ pat_index=overlay_func[pat_index-]+;
  86. }
  87.  
  88. /**
  89. * 如果pat_index已经在末尾了,说明在target上成功匹配pattern
  90. * ,此时tar_index-pat_index就是主串上模式串的起始位置
  91. * */
  92. if(pat_index==pat_len) {
  93. delete [] overlay_func;
  94. return tar_index-pat_index;
  95. }
  96. else {
  97. delete [] overlay_func;
  98. return -;
  99. }
  100. }
  101.  
  102. int main() {
  103. char *target="annbcdanacadsannannanna";
  104. char *pattern="annanna";
  105. printf("\n%d",kmp_func(target, , pattern, ));
  106. return ;
  107. }

参考链接:
http://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm
http://www.matrix67.com/blog/archives/115
http://blog.csdn.net/v_JULY_v/article/details/6111565

笔试算法题(52):简介 - KMP算法(D.E. Knuth, J.H. Morris, V.R. Pratt Algorithm)的更多相关文章

  1. 串匹配算法讲解 -----BF、KMP算法

      参考文章: http://www.matrix67.com/blog/archives/115     KMP算法详解 http://blog.csdn.net/yaochunnian/artic ...

  2. 大话数据结构(十二)java程序——KMP算法及改进的KMP算法实现

    1.朴素的模式匹配算法 朴素的模式匹配算法:就是对主串的每个字符作为子串开头,与要连接的字符串进行匹配.对主串做大循环,每个字符开头做T的长度的小循环,直到成功匹配或全部遍历完成为止. 又称BF算法 ...

  3. 算法-最通俗易懂的KMP算法详解

    有些算法,适合从它产生的动机,如何设计与解决问题这样正向地去介绍.但KMP算法真的不适合这样去学.最好的办法是先搞清楚它所用的数据结构是什么,再搞清楚怎么用,最后为什么的问题就会有恍然大悟的感觉.我试 ...

  4. c算法:字符串查找-KMP算法

    /* *用KMP算法实现字符串匹配搜索方法 *该程序实现的功能是搜索本目录下的所有文件的内容是否与给定的 *字符串匹配,如果匹配,则输出文件名:包含该字符串的行 *待搜索的目标串搜索指针移动位数 = ...

  5. 【数据结构&算法】10-串基础&KMP算法源码

    目录 前言 串的定义 串的比较 串的抽象类型数据 串与线性表的比较 串的数据 串的存储结构 串的顺序存储结构 串的链式存储结构 朴素的模式匹配算法 模式匹配的定义 朴素的匹配方法(BRUTE FORC ...

  6. LeetCode刷题--基础知识篇--KMP算法

    KMP算法 关于字符串匹配的算法,最知名的莫过于KMP算法了,尽管我们日常搬砖几乎不可能去亲手实现一个KMP算法,但作为一种算法学习的锻炼也是很好的,所以记录一下. KMP算法是根据三位作者(D.E. ...

  7. 模式匹配的KMP算法详解

    这种由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现的改进的模式匹配算法简称为KMP算法.大概学过信息学的都知道,是个比较难理解的算法,今天特把它搞个彻彻底底明明白白. 注意到这 ...

  8. 前端如何应对笔试算法题?(用node编程)

    用nodeJs写算法题 咱们前端使用算法的地方不多,但是为了校招笔试,不得不针对算法题去练习呀! 好不容易下定决心 攻克算法题.发现js并不能像c语言一样自建输入输出流.只能回去学习c语言了吗?其实不 ...

  9. 【原创】通俗易懂的讲解KMP算法(字符串匹配算法)及代码实现

    一.本文简介 本文的目的是简单明了的讲解KMP算法的思想及实现过程. 网上的文章的确有些杂乱,有的过浅,有的太深,希望本文对初学者是非常友好的. 其实KMP算法有一些改良版,这些是在理解KMP核心思想 ...

随机推荐

  1. Quartz.NET 快速入门

    官网:http://www.quartz-scheduler.net/ API:http://www.quartz-scheduler.net/documentation/index.html 快速入 ...

  2. Gym 100512F Funny Game (博弈+数论)

    题意:给两个数 n,m,让你把它们分成 全是1,每次操作只能分成几份相等的,求哪一个分的次数最多. 析:很明显,每次都除以最小的约数是最优的. 代码如下: #pragma comment(linker ...

  3. HDU1244:Max Sum Plus Plus Plus

    题目链接:Max Sum Plus Plus Plus 题意:在n个数中取m段数使得这m段数之和最大,段与段之间不能重叠 分析:见代码 //dp[i][j]表示前i个数取了j段的最大值 //状态转移: ...

  4. 洛谷 P4009 汽车加油行驶问题 【最小费用最大流】

    分层图,建k层,设(i,j,0)为点(i,j)的满油状态,全图的流量都是1,因为重复走到一个点没有意义.如果当前点是加油站,那么它向它上左的点连费用为a的边,向下右连费用为a+b的边: 否则,这个点的 ...

  5. zoj 2532 Internship【最小割】

    就是求哪些边在最大流上满流,也就是找割边.把0作为t点,s向所有的1~n连流量为inf的边,其他的边按照流量连.跑一遍最大流,从s顺着有残余流量的正向边dfs打标记fr,从t顺着正向边有残余流量的反向 ...

  6. 自己动手利用CentOS6.5 搭建php环境安装discuz论坛

    1.安装搭建论坛必要的软件 apache php mysql CentOS系统我们可以直接使用 yum install 的方式进行软件安装,腾讯云有提供软件安装源,是同步CentOS官方的安装源,包涵 ...

  7. CF446C [DZY loves Fibonacci]

    Description Transmission Gate 你需要维护一个长度为\(n \leq 300000\) 的数列,兹词两个操作: 1.给一个区间加上一个fibonacci数列,规定\(f[0 ...

  8. _bzoj1500 [NOI2005]维修数列【真·Splay】

    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1500 注意MAX-SUM的时候,不可以是空串. #include <cstdio> ...

  9. Retinex系列之McCann99 Retinex 分类: 图像处理 Matlab 2014-12-03 11:27 585人阅读 评论(0) 收藏

    一.McCann99 Retinex McCann99利用金字塔模型建立对图像的多分辨率描述,自顶向下逐层迭代,提高增强效率.对输入图像的长宽有 严格的限制,要求可表示成 ,且 ,. 上述限制来源于金 ...

  10. 224 Basic Calculator 基本计算器

    实现一个基本的计算器来计算一个简单的字符串表达式. 字符串表达式可以包含左括号 ( ,右括号),加号+ ,减号 -,非负整数和空格 . 假定所给的表达式语句总是正确有效的. 例如: "1 + ...