每周一算法之六——KMP字符串匹配算法
KMP是一种著名的字符串模式匹配算法,它的名称来自三个发明人的名字。这个算法的一个特点就是,在匹配时,主串的指针不用回溯,整个匹配过程中,只需要对主串扫描一遍就可以了。因此适合对大字符串进行匹配。
搜了网上很多KMP的代码下来调试,发现不是下标越界,就是死循环的,相当诡异...最后重新拿起严老师那本《数据结构》来翻,各种费解,有个地方用下标值和字符串下标0的元素做判断,更是诡异了...
过了一天,忽然觉悟了。网上这些代码都是来自《数据结构》或者和他同源的版本的,而它使用的是以下标1为起始的字符串!对这种字符串组织格式,下标0存放的是字符串的长度。
可是如今主流的语言,几乎都是用的下标0作为起始,书本上的代码显然没法用,那就自己重写一个吧。
算法的原理
字符串匹配嘛,无非就是两个指针,分别指向主串和模式串,然后依次往后移,检查是否一致。在遇到不能匹配的情况时(简称“失配”),一般的方法,就是让两个指针回溯,主串指针往后再移动一位,从头开始匹配。这其中做了很多重复劳动,我们可以分析一下:
可以看到模式串在匹配到下标5时失配了。
我们抓出模式串和主串在前方匹配的5个字符,并在模式串部分的前端和主串部分的后端找到了一对最长的相等的字串(不等于原来的串),用阴影标记一下,后面有用。
接着移动模式串,继续匹配:
看出什么规律了么?每次比较,其实都是“abaab”的前端和后端的字串进行比较:
第一回是"abaa"vs"baab"
第二回是"aba"vs"aab"
第三回是"ab"vs"ab"
可见,只有在模式串部分的前端和主串部分的后端重合的时候,才可能继续匹配。
是这样么?当然是的,因为我们之前找出的是最长的,相等的字串!
这样就能把中间无效的对比步骤省略,主串的指针不变,模式串的指针直接跳到下标2继续匹配。这里的下标2就等于最长相等字串的长度。
接着推广到更一般的情形:
假设主串s,模式串patten,s和patten分别在下标i,j处失配,
如果j>0,那么,
显而易见,'si-k...si-1' = 'patten0...pattenk-1',此串长度为k,故下一步模式串指针应当跳转到下标k继续匹配。
在这里,因为'si-k...si-1' = 'pattenj-k...pattenj-1',得到'patten0...pattenk-1' = 'pattenj-k...pattenj-1',所以给定patten和j的情况下,k的值也是固定的。
如果j=0,那么i应当往后挪一位,j不变,重头匹配
至此,对于给定的patten,可以得到一个j->k的映射关系,记为数组next,其中,k = next[j]:
next[j] = Max{ k | 0<=k<j 并且 'patten0...pattenk-1' = 'pattenj-k...pattenj-1' }
当且仅当j == 0时,next[j] = -1(-1其实是没有意义的,在这里为了计算方便)
依照这个定义,已经可以写出一个计算next的弱弱的实现了。不过我先买个关子,先把主串的匹配搞定再说。
主串匹配算法
有了之前的分析,主串匹配的代码基本就可以一蹴而就了(Java代码):
static int Kmp(String s, String patten) {int i = 0, j = -1;int[] next = GetNext(patten);// 待实现while (i < s.length() && j < patten.length()) {if (j == -1 || s.charAt(i) == patten.charAt(j)) {i++;j++;} else {j = next[j];// 失配时跳转}}if (j == patten.length()) // 完全匹配return i - j;return -1; }
这儿有一处很巧妙地的地方:
next[0]是恒为-1的,所以如果在下标0处失配,则下一次循环j等于-1,i就会在循环中指向下一个字符,j也恢复为0。
模式串的next数组生成算法
看下面这张图
假设模式串上的下标i,模式串下的下标j,那么
显然next[5] = 2是由patteni=4 = pattenj=1推出的,
推广到一般的情况,也就是说当patten与自身错位匹配时,当他们在i,j(i>j)处匹配时,
此时可以得到next[i+1] = j+1
如果j = 0时就失配了的话,自然next[i+1]应当等于0
至此,写出代码也就不难了,有些小技巧却要注意一下(Java代码):
static int[] GetNext(String s) {int i = 0, j = -1;int[] next = new int[s.length()];next[0] = -1; // 这个初始化时必须的while( i<s.length()-1){if( j == -1 || s.charAt(i) == s.charAt(j)){i++;j++;next[i] = j;}else{j = next[j];// 当j在下标零处失配,代码会怎么执行呢?}}return next; }
这个求next数组的方式和KMP算法的主体是不是很像呢?
每周一算法之六——KMP字符串匹配算法相关推荐
- 字符串匹配KMP算法设计C语言,KMP字符串匹配算法笔记
网上有很多解释KMP算法的文章,A_B_C_ABC的这篇很详细,反复看了好几遍,总算理解了个大概,但是总觉得没那么爽快.其实,一种算法各人有各人的理解方法,找到适合自己理解的才容易记住.下面是我对这个 ...
- KMP字符串匹配算法理解(转)
一.引言 主串(被扫描的串):S='s0s1...sn-1',i 为主串下标指针,指示每回合匹配过程中主串的当前被比较字符: 模式串(需要在主串中寻找的串):P='p0p1...pm-1',j 为模式 ...
- diff算法阮一峰_【重学数据结构与算法(JS)】字符串匹配算法(三)——BM算法
前言 文章的一开头,还是要强调下字符串匹配的思路 将模式串和主串进行比较 从前往后比较 从后往前比较 2. 匹配时,比较主串和模式串的下一个位置 3. 失配时, 在模式串中寻找一个合适的位置 如果找到 ...
- 大量的数据做字符串匹配_【重学数据结构与算法(JS)】字符串匹配算法(三)——BM算法...
前言 文章的一开头,还是要强调下字符串匹配的思路 将模式串和主串进行比较 从前往后比较 从后往前比较 2. 匹配时,比较主串和模式串的下一个位置 3. 失配时, 在模式串中寻找一个合适的位置 如果找到 ...
- 算法之「字符串匹配算法」
前言 一说到两个字符串匹配,我们很自然就会想到用两层循环来匹配,用这种方式就可以实现一个字符串是否包含另一个字符串了,这种算法我们称为 BF算法. BF算法 BF算法,即暴力(Brute Force) ...
- Java实现算法导论中KMP字符串匹配算法
"前缀"和"后缀". "前缀"指除了最后一个字符以外,一个字符串的全部头部组合:"后缀"指除了第一个字符以外,一个字符串 ...
- C++ : KMP 字符串匹配算法
在传统字符串匹配中我们求得字符串p出现在字符串s中的位置.我们把字符串s称为主串,字符串p称为模式串. KMP算法的原理简单来说就是匹配的时候不回溯主串的指针i,而只回溯模式串指针j ,即匹配过程中, ...
- 【KMP】KMP 字符串匹配算法
文章目录 1.概述 2.暴力匹配算法 3.KMP算法 3.1 案例 3.2 部分匹配表 3.3 匹配 M.参考 本文为博主九师兄(QQ:541711153 欢迎来探讨技术)原创文章,未经允许博主不允许 ...
- 字符串匹配算法(KMP)
文章目录 1. KMP由来 2. KMP算法基本原理 3. 代码 4. Leetcode 28. 实现 strStr() 1. KMP由来 上一节说的BM算法是最高效.最常用的字符串匹配算法. 最知名 ...
最新文章
- perl 的基本数据类型
- maven 内置参数
- POJ 2255 Tree Recovery 二叉树恢复
- 流媒体服务器 客户端播放器方案推荐
- 生产环境JVM内存溢出案例分析
- Qt 编译出错 Could not create directory
- Linux两个进程交换信息,如何在Linux中的进程之间交换二进制数据
- 查询进程打开的文件(转)
- Linux 驱动面试题总结
- 在 eclipse 中设置每行的字数
- 使用代码更新 UIVersion 属性
- 阿里云大学python教程下载_阿里大学开放 11 门免费 Python 视频课程
- java用this-gt;,java基础之十四-&gt;常用类
- RadASM 颜色配置
- 读书笔记(13)STP
- Yii框架验证码不显示、不刷新、验证不正确的一些解决办法
- 国内直接下载google play谷歌商店apk安装包的网站【https://apkpure.com/】
- linux中断子系统(基于imx6ul arm32分析)
- 5.3.3—二叉查找树—Validate Binary Sear Tree
- 高通WLAN框架学习(17)-- NIO和PNO
热门文章
- js根据应纳税所得额计算税金
- 矩形排样 matlab,二维多阶段矩形剪切排样算法(精).pdf
- mysql无法打开和锁定权限表_MySQL在连续查询后丢弃连接
- python的字符串_百度资讯搜索_python的字符串
- 华中科技大学应用高等工程数学_专业解析【第152期】| 机械电子工程课程设置及研究方向...
- 学完计算机的感想300,计算机实训总结计算机实训心得300
- 胆战心惊形容什么_阿里员工感慨:加班累死累活,还胆战心惊,难道IT公司都这个样?...
- c语言数组方式实现静态循环队列
- Canvas绘制星球轨迹移动
- java socket 远程调用_SpringBoot使用Netty实现远程调用的示例