知乎高赞分析

作者:逍遥行

链接:https://www.zhihu.com/question/21923021/answer/37475572
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

角色:

甲: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++实现的更多相关文章

  1. KMP算法分析

    KMP是一种复杂度较低的字符串比较算法.基本思路是对欲匹配字符串进行预处理,分析当k位匹配时可以后移的位数,所得的数构成该字符串的特征向量. 求特征向量Next int* Next(string p) ...

  2. 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 ...

  3. KMP算法和bfprt算法总结

    目录 1 KMP算法 1.1 KMP算法分析 1.2 KMP算法应用 题目1:旋转词 题目2:子树问题 2 bfprt算法 2.1 bfprt算法分析 2.2 bfprt算法应用 1 KMP算法 大厂 ...

  4. 算法:KMP算法

    算法:KMP排序 算法分析 KMP算法是一种快速的模式匹配算法.KMP是三位大师:D.E.Knuth.J.H.Morris和V.R.Pratt同时发现的,所以取首字母组成KMP. 少部分图片来自孤~影 ...

  5. 字符串匹配:KMP算法

    一.原理: KMP算法是由Knuth,Morris,Pratt共同提出的模式匹配算法,其对于任何模式和目标序列,都可以在线性时间内完成匹配查找,而不会发生退化,是一个非常优秀的模式匹配算法.朴素算法( ...

  6. Java数据结构之字符串模式匹配算法---KMP算法

    本文主要的思路都是参考http://kb.cnblogs.com/page/176818/ 如有冒犯请告知,多谢. 一.KMP算法 KMP算法可以在O(n+m)的时间数量级上完成串的模式匹配操作,其基 ...

  7. 程序员必会算法-KMP算法

    KMP算法是一种优秀的字符串匹配算法,字符串匹配的常规算法是一步一步进行移位和比较操作,直至找到完全相匹配的字符串. 下面通过一个例子,为大家仔细说明KMP算法的使用和思路: 问题: 在字符串“DEA ...

  8. 算法导论————KMP

    [例题传送门:caioj1177] KMP模版:子串是否出现 [题意]有两个字符串SA和SB,SA是母串,SB是子串,问子串SB是否在母串SA中出现过.如果出现过输出第一次出现的起始位置和结束位置,否 ...

  9. 数据结构4_java---顺序串,字符串匹配算法(BF算法,KMP算法)

    1.顺序串 实现的操作有: 构造串 判断空串 返回串的长度 返回位序号为i的字符 将串的长度扩充为newCapacity 返回从begin到end-1的子串 在第i个字符之前插入字串str 删除子串 ...

随机推荐

  1. WPF程序发布有关事项

  2. js中声明函数的区别

    在JS中有两种定义函数的方式, 1是var aaa=function(){...} 2是function aaa(){...} var 方式定义的函数,不能先调用函数,后声明,只能先声明函数,然后调用 ...

  3. Program Transformation Semantics (程序转换语义学)

    本文是Inside The C++ Object Model Chapter 2 部分的读书笔记.讨论编译器调用拷贝构造函数时的策略(如何优化以提高效率),侯捷称之为"程序转化的语义学&qu ...

  4. vue + ts Vuex篇

    Vuex对Typescript的支持,仍十分薄弱,官方库只是添加了一些.d.ts声明文件,并没有像vue 2.5这样内置支持. 第三方衍生库 vuex-typescript, vuex-ts-deco ...

  5. vue+ts搭建项目

    Tip: 为了避免浪费您的时间,本文符合满足以下条件的同学借鉴参考 1.本文模版不适用于小型项目,两三个页面的也没必要用vue2.对typescript.vue全家桶能够掌握和运用 此次项目模版主要涉 ...

  6. Android学习_Selector

    Selector 实现组件在不同状态下不同的文字颜色.背景颜色或图片的切换,使用十分方便. 1. 创建方法 第一种:在XML中直接创建selector的XML文件,容易掌握,简单但是不灵活,较为常用. ...

  7. TCP->IP输出 之 ip_queue_xmit、ip_build_and_send_pkt、ip_send_unicast_reply

    概述 ip_queue_xmit是ip层提供给tcp层发送回调,大多数tcp发送都会使用这个回调,tcp层使用tcp_transmit_skb封装了tcp头之后,调用该函数,该函数提供了路由查找校验. ...

  8. 读取天气信息,并通过QQ邮箱发送至指定邮箱

    from email.mime.text import MIMEText from email.header import Header from smtplib import SMTP_SSL im ...

  9. python 购物车+用户认证程序

    创建文件a.txt,b.txt.c.txt用于存放应该持续保存的信息 a.txt :用户密码输入错误3次就锁定 b.txt :购物时的活动,每个用户只能参与一次 c:txt :购物完后的发票在这里查看 ...

  10. CentOS7 开机启动脚本与命令后台运行

    一.& 在 Linux 命令后加上 &  可以在后台运行 二.nohup 对 SIGHUP 信号免疫,对 SIGINT 信号不免疫,可用 shopt | grep hup 查看. 当关 ...