kmp算法分析和C++实现
知乎高赞分析
作者:逍遥行
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
角色:
甲:abbaabbaaba
乙:abbaaba
乙对甲说:「帮忙找一下我在你的哪个位置。」
甲从头开始与乙一一比较,发现第 7 个字符不匹配。
要是在往常,甲会回退到自己的第 2 个字符,乙则回退到自己的开头,然后两人开始重新比较。这样的事情在字符串王国中每天都在上演:不匹配,回退,不匹配,回退,……
但总有一些妖艳字符串要花出自己不少的时间。
上了年纪的甲想做出一些改变。于是甲把乙叫走了:「你先一边玩去,我自己研究下。」
甲给自己定了个小目标:发生不匹配,自己不回退。
甲发现,若要成功与乙匹配,必须要匹配 7 个字符。也就是说,就算自己回退了,在后续的匹配流程中,肯定还要匹配自己的第 7 个字符。
当在甲的某个字符 c 上发生不匹配时,甲即使回退,最终还是会重新匹配到字符 c 上。
那干脆不回退,岂不美哉!
甲不回退,乙必须回退地尽可能少,并且乙回退位置的前面那段已经和甲匹配,这样甲才能不用回退。
如何找到乙回退的位置?
「不匹配发生时,前面匹配的那一小段 abbaab 于我俩是相同的」,甲想,「这样的话,用 abbaab 的头部去匹配 abbaab 的尾部,最长的那段就是答案。」
具体来说,
abbaab 的头部有 a, ab, abb, abba, abbaa(不包含最后一个字符。下文称之为「前缀」)
abbaab 的尾部有 b, ab, aab, baab, bbaab(不包含第一个字符。下文称之为「后缀」)
这样最长匹配是 ab,乙回退到第三个字符和甲继续匹配。
「要计算的内容只和乙有关」,甲想,「那就假设乙在所有位置上都发生了不匹配,乙在和我匹配之前把所有位置的最长匹配都算出来(算个长度就行),生成一张表,之后我俩发生不匹配时直接查这张表就行。」
据此,甲总结出了一条甲方规则:
所有要与甲匹配的字符串,必须先自身匹配:对每个子字符串 [0...i],算出其「相匹配的前缀与后缀中,最长的字符串的长度」。
甲把乙叫了回来,告诉他新出炉的甲方规则。
「小 case,我对自己还不了解吗」,乙眨了一下眼睛,「那我回退到第三个字符和你继续匹配就行了」。
原理分析
在所有字符串匹配算法里最知名的一种非kmp算法莫属,很多时候提到字符串匹配我们首先想到的就是kmp算法。但kmp算法是出了名的不好懂,个人也是一直理解不好。而上面知乎高赞的分析我觉得用来理解KMP算法非常形象,对于理解kmp算法的核心原理非常有帮助。
简单总结上面的分析:在模式串和主串匹配过程中,当遇到坏字符后,主串不回退,用已经匹配的字符串的头部匹配其尾部,即前缀匹配后缀,直接回退到最长匹配前缀的尾部位置。
很明显,已匹配的字符串其实就是模式串的一部分,所以如何计算已匹配的字符串的最长匹配,其实可以不涉及主串。而模式串每一部分的最长匹配前缀的尾部位置其实预先计算好,在模式串和主串匹配的过程中直接拿过来用就好。kmp算法就是提前构建一个数组,用来存储模式串中每个前缀的最长可匹配前缀子串的结尾字符的下标。我们把这个数组定义为next数组,很多书还为这个数组起了一个名字叫失效数组(failure function)。
数组的下标是每个前缀结尾字符下标,数组的值是这个前缀的最长可匹配前缀子串的结尾字符的下标。这句话比较难理解,看下面是极客时间王争专栏的例子就应该一看就懂了。

至此,我觉得已经足够从原理上理解kmp算法了,具体实现的细节下面的的C++代码实现已经足够清楚,此处不再赘述。本人理解kmp算法过程非常坎坷,之前自以为理解了其实根本没弄懂,写的博文纯粹也是误人子弟,所以现在重新修改后有种如释重负的感觉。这里,再次感谢知乎的回答的作者和王争老师帮助!
代码实现
#ifndef _KMP_H
#define _KMP_H #include <string>
#include <vector>
#include <list> std::vector<int> getNext(std::string &s) {
int k = -;
int i = ;
std::vector<int> next(s.size(), -);
for (int i = ; i < s.size(); ++i) {
while (k != - && s[k + ] != s[i]) {
k = next[k];
}
if (s[k + ] == s[i]) {
++k;
}
next[i] = k;
}
return next;
} std::list<int> kmp(std::string &text, std::string &s) {
std::list<int> posList;
std::vector<int> next = getNext(s);
int k = -;
for (int i = ; i < text.size(); ++i) {
while (k != - && s[k + ] != text[i]) {
k = next[k];
}
if (s[k + ] == text[i]) {
k++;
}
if (k == s.size() - ) {
posList.push_back(i - k);
k = next[k];
}
}
return posList;
} #endif
kmp算法分析和C++实现的更多相关文章
- KMP算法分析
KMP是一种复杂度较低的字符串比较算法.基本思路是对欲匹配字符串进行预处理,分析当k位匹配时可以后移的位数,所得的数构成该字符串的特征向量. 求特征向量Next int* Next(string p) ...
- poj 2752 Seek the Name, Seek the Fame【KMP算法分析记录】【求前后缀相同的子串的长度】
Seek the Name, Seek the Fame Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 14106 Ac ...
- KMP算法和bfprt算法总结
目录 1 KMP算法 1.1 KMP算法分析 1.2 KMP算法应用 题目1:旋转词 题目2:子树问题 2 bfprt算法 2.1 bfprt算法分析 2.2 bfprt算法应用 1 KMP算法 大厂 ...
- 算法:KMP算法
算法:KMP排序 算法分析 KMP算法是一种快速的模式匹配算法.KMP是三位大师:D.E.Knuth.J.H.Morris和V.R.Pratt同时发现的,所以取首字母组成KMP. 少部分图片来自孤~影 ...
- 字符串匹配:KMP算法
一.原理: KMP算法是由Knuth,Morris,Pratt共同提出的模式匹配算法,其对于任何模式和目标序列,都可以在线性时间内完成匹配查找,而不会发生退化,是一个非常优秀的模式匹配算法.朴素算法( ...
- Java数据结构之字符串模式匹配算法---KMP算法
本文主要的思路都是参考http://kb.cnblogs.com/page/176818/ 如有冒犯请告知,多谢. 一.KMP算法 KMP算法可以在O(n+m)的时间数量级上完成串的模式匹配操作,其基 ...
- 程序员必会算法-KMP算法
KMP算法是一种优秀的字符串匹配算法,字符串匹配的常规算法是一步一步进行移位和比较操作,直至找到完全相匹配的字符串. 下面通过一个例子,为大家仔细说明KMP算法的使用和思路: 问题: 在字符串“DEA ...
- 算法导论————KMP
[例题传送门:caioj1177] KMP模版:子串是否出现 [题意]有两个字符串SA和SB,SA是母串,SB是子串,问子串SB是否在母串SA中出现过.如果出现过输出第一次出现的起始位置和结束位置,否 ...
- 数据结构4_java---顺序串,字符串匹配算法(BF算法,KMP算法)
1.顺序串 实现的操作有: 构造串 判断空串 返回串的长度 返回位序号为i的字符 将串的长度扩充为newCapacity 返回从begin到end-1的子串 在第i个字符之前插入字串str 删除子串 ...
随机推荐
- 数据类型之字符串类型与Number类型
㈠字符串类型 ⑴在JS中字符串需要使用引号引起来 ⑵使用双引号或单引号都可以,但是不要混着用 ⑶引号不能嵌套,双引号不能放双引号,单引号不能放单引号 ⑷在字符串中,可以使用“\”作为转义字符,当表示一 ...
- 51 Nod 1116 K进制下的大数
1116 K进制下的大数 基准时间限制:1 秒 空间限制:131072 KB 分值: 20 难度:3级算法题 收藏 关注 有一个字符串S,记录了一个大数,但不知这个大数是多少进制的,只知道这个数 ...
- 十进制数转N进制c++实现
编写一个算法,将一个非负的十进制整数N转换为另一个基数为B的B进制整数. #include <iostream> #include<string.h> using namesp ...
- MySQL_(Java)使用preparestatement解决SQL注入的问题
MySQL_(Java)使用JDBC向数据库发起查询请求 传送门 MySQL_(Java)使用JDBC创建用户名和密码校验查询方法 传送门 MySQL数据库中的数据,数据库名garysql,表名gar ...
- HighCharts 动态设置 series
var series = new Array(); var map = response.extend.map; $.each(map, function (key,values) { series. ...
- PHP ob_get_level嵌套输出缓冲
PHP的输出缓存是可以嵌套的.用ob_get_level()就可以输出嵌套级别. 测试发现在cli和浏览器下输出结果不一样(PHP5.4). ob_level1.png手册说明如下: ob_get_l ...
- Anaconda官网下载太慢/出错,以及Anaconda下载包又慢又出错的总体方法,应该如何快速下载,使用上海科技大学的开源镜像站即可
1.最新更新:清华源和中科大源都已经挂了,不要再用他们的镜像源了!!!用上海科技大学的镜像!!!! 2.其次,CSDN上大多的快速装包法都在现在(2019.5.11)出现了问题,也不全,本文是亲自实践 ...
- Qt第三方库libvlc-qt——ubuntu上编译、安装,测试
cmake 3.0编译安装(最低版本要求): sudo apt-get install ncurses-dev sudo apt-get install build-essential 下载cma ...
- android canvas drawtext 字高
Paint pFont = new Paint(); Rect rect = new Rect(); pFont.getTextBounds("豆", 0, 1, rect); L ...
- WebStrom编程小技巧--HTML快速创建指定id或者类名的div
打印div标签快速方法:“先打出#yz,然后Tab键补全即可获得<div id="yz"></div>同理:我们也可以先打出“.tz"然后Tab键 ...