kmp算法的理解与实现

博客分类:

  • algorithms
算法 

    KMP算法曾被我戏称为看毛片算法,当时笑喷......大三那个时候硬着头皮把算法导论的kmp算法啃完,弄懂了kmp算法

的原理,甚至还写出了代码,这几天再次温习的时候,发现忘得比较彻底。我总结,学算法不能只对着书本学理论,而应该

用自己的理解去看清算法的本质,最好用文字把你的理解记录下来,这样才能做到活学活用,而且不容易忘。写这篇博客就是想把自己这几天的思路记下来。

一 kmp算法为什么比传统的字符串匹配算法快

假设文本T = y1y2y3....yn, 模式 P = p1p2p3...pm, 传统的匹配算法把位移为0,1,...n-m时的文本依次跟P比较,每次比较最多花费O(m)的时间,算法的复杂度为O((n-m+1)*m)。这种算法没有利用匹配过的信息,每次都从头开始比较,速度很慢。而kmp算法充分利用了之前的匹配信息,从而避免一些明显不合法的位移。加快匹配过程。来看一个例子:

#########000xxxx000######                       文本T

|<---- s ---->|000xxxx000~~~                              模式P

假设位移为s时,T和P匹配了红色部分的字符,即匹配到了模式P的前10个字符,如果按照传统的匹配方法,下一步就是从位移s+1开始比较,而kmp算法则直接从位移s+7开始比较,而且断定:位移s+7对应的串和模式P的前3个字符是相同的,可

以不用比较,直接从第4个字符开始比较,这种跳跃式的匹配是不是比传统匹配方法快很多,如下图所示:

#########000xxxx000######                       文本T

|<-------- s+7-------->| 000xxxx000~~~               模式P

那么kmp是如何实现这种跳跃的呢?注意到红色部分的字符,即模式P的前10个字符,有一个特点:它的开始3个字符和末尾

3个字符是一样的,又已知文本T也存在红色部分的字符,我们把位移移动 10-3 = 7个位置,让模式P的开始3个字符对准文本

T红色部分的末尾3个字符,那么它们的前3个字符必然可以匹配。

二 构造前缀数组

上面的例子是文本T和模式P匹配了前面10个字符的情况下发生的,而且我们观察到模式P的前缀P10中,它的开始3个字符和末尾3个字符是一样的。如果对于模式P的所有前缀P1,P2...Pm,都能求出它们首尾有多少个字符是一样的,当然相同的字

符数越多越好,那么就可以按照上面的方法,进行跳跃式的匹配。

定义:

Pi表示模式P的前i个字符组成的前缀, next[i] = j表示Pi中的开始j个字符和末尾j个字符是一样的,而且对于前缀Pi来说,这样

的j是最大值。next[i] = j的另外一个定义是:有一个含有j个字符的串,它既是Pi的真前缀,又是Pi的真后缀

规定:

next[1] = next[0] = 0

next[i]就是前缀数组,下面通过1个例子来看如何构造前缀数组。

例子1:cacca有5个前缀,求出其对应的next数组。

前缀2为ca,显然首尾没有相同的字符,next[2] = 0

前缀3为cac,显然首尾有共同的字符c,故next[3] = 1

前缀4为cacc,首尾有共同的字符c,故next[4] = 1

前缀5为cacca,首尾有共同的字符ca,故next[5] = 2

如果仔细观察,可以发现构造next[i]的时候,可以利用next[i-1]的结果。假设模式已求得next[10] = 3,如下图所示:

000#xxx000         前缀P10

000                        末尾3个字符

根据前缀函数的定义:next[10] = 3意味着末尾3个字符和P10的前3个字符是一样的

为求next[11],可以直接比较第4个字符和第11个字符,如下图所示:蓝色和绿色的#号所示,如果它们相等,则

next[11] = next[10]+1 = 4,这是因为next[10] = 3保证了前缀P11和末尾4个字符的前3个字符是一样的.

000#xxx000#       前缀P11

000#                      末尾4个字符

所以只需验证第4个字符和第11个字符。但如果这两个字符不想等呢?那就继续迭代,利用next[next[10] = next[3]的值来求

next[11]。代码如下:

C代码  
  1. void compute_prefix(int *next, char *p)
  2. {
  3. int     i, n, k;
  4. n = strlen(p);
  5. next[1] = next[0] = 0;
  6. k = 0;      /* 第i次迭代开始之前,k表示next[i-1]的值 */
  7. for (i = 2; i <= n; i++) {
  8. for (; k != 0 && p[k] != p[i-1]; k = next[k]);
  9. if (p[k] == p[i-1]) k++;
  10. next[i] = k;
  11. }
  12. }

三 模拟KMP的查找过程

这里实现的算法与算法导论中的不一样,我觉得这种方法更加直观,思路就是模拟第1部分介绍的方法,每次匹配的时候

都利用上一次匹配信息,对于模式P,从第next[q]个字符开始比较,对于文本T,用一个变量s指示将要从哪个位置开始比较

,迭代开始之前,就从s这个位置开始。

C代码  
  1. void kmp_match(char *text, char *p, int *next)
  2. {
  3. int     m, n, s, q;
  4. m = strlen(p);
  5. n = strlen(text);
  6. q = s = 0;  /* q表示上一次迭代匹配了多少个字符,
  7. s表示这次迭代从text的哪个字符开始比较 */
  8. while (s < n) {
  9. for (q = next[q]; q < m && p[q] == text[s]; q++, s++);
  10. if (q == 0) s++;
  11. else if (q == m) {
  12. printf("pattern occurs with shift %d\n", s-m);
  13. }
  14. }
  15. }

四 测试

C代码  
  1. int main()
  2. {
  3. int     next[101], n;
  4. char    *p = "ca";
  5. char    *text = "cacca";
  6. compute_prefix(next, p);
  7. kmp_match(text, p, next);
  8. return 0;
  9. }

转载于:https://www.cnblogs.com/daimadebanyungong/p/4758322.html

一篇别人写的Kmp算法的讲解,多看多得相关推荐

  1. 转载几篇别人写的皮肤类控件的技术文章

    转载几篇别人写的皮肤类控件的技术文章 原连接:http://blog.sina.com.cn/s/blog_4c3538470100ezhu.html 实现控件的透明背景 很多情况下,我们需要控件 的 ...

  2. (转)KMP算法原理讲解及模板C实现

    原作者:v_JULY_v 1. 引言 本KMP原文最初写于2年多前的2011年12月,因当时初次接触KMP,思路混乱导致写也写得混乱.所以一直想找机会重新写下KMP,但苦于一直以来对KMP的理解始终不 ...

  3. KMP算法详细讲解(看完不会请打我)

    文章目录 前言 一:情景导入-如何快速在一个主串找到目标字符串 二:详解KMP (1)暴力匹配的缺点 (2)最长相同前缀和后缀 (3)究竟怎么回溯 (3)next数组 (4)求解next数组 A:ne ...

  4. 字符串匹配KMP算法的讲解C++

    转自http://blog.csdn.net/starstar1992/article/details/54913261 也可以参考http://blog.csdn.net/liu940204/art ...

  5. 第2部分 字符串算法(提高篇)--第2章 KMP算法1469:似乎在梦中见过的样子

    1469:似乎在梦中见过的样子 时间限制: 1000 ms 内存限制: 65536 KB 提交数: 476 通过数: 159 [题目描述] 原题来自:2014 年湖北省队互测 Week2 「Madok ...

  6. 详解KMP算法原理,以及完整java与C++实现

    点击此处学习更多算法与通信知识 作者 | labuladong 来源 | labuladong KMP 算法(Knuth-Morris-Pratt 算法)是一个著名的字符串匹配算法,效率很高,但是确实 ...

  7. kmp算法next计算方法_KMP 算法详解

    KMP 算法(Knuth-Morris-Pratt 算法)是一个著名的字符串匹配算法,效率很高,但是确实有点复杂. 很多读者抱怨 KMP 算法无法理解,这很正常,想到大学教材上关于 KMP 算法的讲解 ...

  8. 【每日一算法】KMP算法,看不懂算我输!

    微信改版,加星标不迷路! 每日一算法-KMP算法详解 作者:poll的笔记 阅读目录 1 字符串匹配 2 KMP算法 1 字符串匹配 字符串匹配是计算机的基本任务之一. 字符串匹配是什么?举例来说,有 ...

  9. KMP算法具体解释(转)

    作者:July. 出处:http://blog.csdn.net/v_JULY_v/. 引记 此前一天,一位MS的朋友邀我一起去与他讨论高速排序,红黑树,字典树,B树.后缀树,包含KMP算法,只有在解 ...

  10. 为什么说在KMP算法中文本串中的每个字符都是需要进行比较操作的?

     KMP算法需要计算一个shift或者next表,这个表是一个部分匹配表,通过这个next表来计算当字符不匹配的时候移动的位数,这个移动位数的计算公式为 移动位数 = 已匹配的字符数 - 对应的n ...

最新文章

  1. Java进程占用内存超高分析
  2. python自动华 (十四)
  3. android 音乐播放器专辑图片旋转,如何在我的音乐播放器(Android)中显示专辑封面?...
  4. 收录对网站优化起到什么作用?
  5. 星之卡比镜之迷宫机器人_迷宫武器盘点 | 是兄弟,就拿大宝剑砍我!
  6. 计算机应用计算题(88)10,计算机应用考试习题(88页)-原创力文档
  7. 哪吒票房逼近40亿,用python爬取哪吒短评分析
  8. X-Magic Pair gcd,剪枝(1600)
  9. SecureCRT 连接虚拟机Linux
  10. 蚂蚁借呗和京东金条全面对比,哪个更划算?
  11. react-router-dom@6获取路由传参
  12. 将搜索二叉树转换为链表_将给定的二叉树转换为双链表(DLL)
  13. xml 导入SQL Server 2005
  14. AIX LV删除后,ORACLE数据库文件全部恢复成功
  15. 全球与中国调频广播发射机市场深度研究分析报告
  16. unity3d 双人巡逻兵网络游戏
  17. 操作系统内存及内存管理方式
  18. c语言中结构体定义中的“冒号”
  19. 计算机工程与应用 审稿费,计算机工程与应用杂志
  20. Java面向对象之线程相关概念 和 线程基本使用

热门文章

  1. 尽点力,虽然也不一定有用
  2. spark 三种部署模式的区别对比
  3. toString和valueOf使得对象访问时显示一个特定格式的字符串,但是可以进行数字运算...
  4. GC类型以及不同类型GC的搭配 1
  5. [转]如何编程实现 2 + 2 = 5?
  6. Oracle数据把持和控制言语详解-1
  7. ORACLE执行计划入门
  8. AcWing 166. 数独
  9. ethtool查看网卡以及修改网卡配置
  10. Laravel Request 和 Laravel Input 常用操作方法