一、介绍

烤馍片KMP算法是用来处理字符串匹配问题的。比如说给你两个字符串A,B,问B是不是A的子串?

比如,eg就是aeggx的子串

一般讲字符串A称为主串,用来匹配的B串称为模式串

定义n为字符串A的长度,m为字符串B的长度(m≤n)

如果用暴力枚举法,时间复杂度为O(NM)

而KMP算法的时间复杂度在最坏的情况下为O(N),十分搞笑高效

↑如果看到这张图饿了,去吃饭,吃完饭再来学KMP

二、烤馍片的流程

step1:把馍片做出来(要想烤馍片,首先得有馍片可以烤)

假设A=“xzxzxqxzxzq”,B=“xzxqxz”,一起来看看这两个馍片字符串如何匹配

step2:烤箱预热,馍片上撒调料

这两个馍片字符串都有各自的调料。A的调料指针是i,B的调料指针是j。

step3:扔到烤箱里,烤!

在高温的作用下,烤箱里发生了微妙的反应,两块馍片也慢慢烤熟,香气充盈……

现在开始烤馍片匹配字符串

假设现在A[i-j+1…i]和B[1…j]两个馍片已经烤熟了两个长度均为j的字符串完全匹配了

很容易想到,下面一步要继续匹配A[i+1]和B[j+1]

当A[i+1]=B[j+1](相同)时,i和j均加一并继续烤重复以上步骤

当A[i+1]≠B[j+1](不相同)时

KMP算法的策略是减小j的值使得A[i-j+1…i]和B[1…j]依然匹配并继续尝试A[i+1]和B[j+1]的值

以样例为例:

i = 1  2  3  4  5  6  7  8  9 10 11

A= x  z  x  z   x  q  x  z  x  z   q

B= x  z  x  q   x  z

j= 1   2  3  4   5  6

从头烤匹配,i=j=2时都没问题

当i=j=3时,A[i+1]='z',B[j+1]='q',A[i+1]≠B[j+1]

那么我们就要减小j值不然就烤糊了

设j减小后变为k

那么k应该是多少呢

由于当前的字符串是已经烤好了的已匹配的,所以k值要尽可能的大,这样馍片就会更好吃匹配的就会尽可能长。

此外,新的k值还要保证B[1…j]中的头k个字符和后k个字符相同

个人认为这里比较难理解,结合图像来看:

如图,A和B分别以i,j结尾的一段是匹配的,为了可以继续烤匹配,B[1…k]和A[i-k+1…i]也要是匹配的(这样才可以继续匹配A[i+1]和B[k+1]),因此,B[1…j]的开头k个字符和末尾k个字符必须要匹配。

于是图就变成了这样:

以样例为例,B[1…3]=xzx,开头一个和最后一个字符都是x,恰巧A[4]=B[2],所以j的值变为1,然后就可以继续烤匹配了

i = 1  2  3  4  5  6  7  8  9 10 11

A= x  z  x  z   x  q  x  z  x  z   q

B= x  z  x  q   x  z

j= 1   2  3  4   5

注意:有的时候直到j=0都无法匹配,这时需要增加i并忽略j,直到A[i]=B[1]为止。

从样例可以看出,k的值与i无关,只与j有关,且每个j值仅对应一个k值。因此,我们可以开一个数组提前将每个j对应的k值存起来,这就是KMP算法中的nxt数组(由于C++中变量名叫next有一些问题。所以通常叫nxt)。nxt[j]表示当匹配到B数组第j个字母而第j+1个字母不能匹配时,k值最大是多少。记住,k值应满足B[1…k]=B[j-k+1…j]且尽可能大(k<j)。

然后,就可以继续烤匹配了。

step4:出炉

当馍片烤好了,就可以出炉了

当j=m时,就匹配完成了

此时依题目要求而定

若题目只要回答B是不是A的子串,输出,结束

若题目要寻找B作为子串出现的次数,则继续寻找

↑香香的烤面筋馍片

下面是代码实现

匹配,输出位置:

//这里数组从1开始
j=;
for(i=;i<n;i++)
{
while(j> && a[i+]!=b[j+]) j=nxt[j];//j未减小到0且不能继续匹配,减小j的值
if(a[i+]==b[j+]) j++;//能继续匹配,j的值增加
//若j==0仍不能匹配,由于循环i的值会自动增加
if(j==m)//找到一处匹配
{
printf("%d\n",i+-m+);//输出子串在主串中的位置
j=nxt[j];//继续匹配
}
}

这是代码1

如果若干子串在主串中的位置不能重复,只需将j=nxt[j]改成j=0即可:

//这里数组从1开始
j=;
for(i=;i<n;i++)
{
while(j> && a[i+]!=b[j+]) j=nxt[j];//j未减小到0且不能继续匹配,减小j的值
if(a[i+]==b[j+]) j++;//能继续匹配,j的值增加
//若j==0仍不能匹配,由于循环i的值会自动增加
if(j==m)//找到一处匹配
{
printf("%d\n",i+-m+);//输出子串在主串中的位置
j=;//从头开始匹配,保证不重复
}
}

这是代码2

预处理nxt数组:

//这里数组从1开始
p[]=j=;
for(i=;i<m;i++)
{
while(j> && b[i+]!=b[j+]) j=nxt[j];//j未减小到0且不能继续匹配,退一步
if(b[i+]==b[j+]) j++;//能继续匹配,j的值增加
//若j==0仍不能匹配,由于循环i的值会自动增加
nxt[i+]=j;//nxt数组赋值
}

这是代码3

有没有觉得预处理和匹配的代码很像?因为预处理的过程其实就是B串一个“自我匹配”的过程。预处理和烤的都是馍片能不像吗

于是美味的馍片就烤好了

一不小心好像烤糊了

推荐例题の传送门:

洛谷P3375 【模板】KMP字符串匹配

洛谷UVA10298 Power Strings

洛谷P4391 [BOI2009]Radio Transmission 无线传输

本文部分图片来源于网络

部分内容参考《信息学奥赛一本通.提高篇》第二部分第二章 KMP算法

若需转载,请注明https://www.cnblogs.com/llllllpppppp/p/9371218.html

~祝大家编程顺利~

浅谈KMP算法的更多相关文章

  1. 浅谈KMP算法及其next[]数组

    KMP算法是众多优秀的模式串匹配算法中较早诞生的一个,也是相对最为人所知的一个. 算法实现简单,运行效率高,时间复杂度为O(n+m)(n和m分别为目标串和模式串的长度) 当字符串长度和字符集大小的比值 ...

  2. 单模式串匹配----浅谈kmp算法

    模式串匹配,顾名思义,就是看一个串是否在另一个串中出现,出现了几次,在哪个位置出现: p.s.  模式串是前者,并且,我们称后一个 (也就是被匹配的串)为文本串: 在这篇博客的代码里,s1均为文本串, ...

  3. 【字符串算法3】浅谈KMP算法

    [字符串算法1] 字符串Hash(优雅的暴力) [字符串算法2]Manacher算法 [字符串算法3]KMP算法 这里将讲述  [字符串算法3]KMP算法 Part1 理解KMP的精髓和思想 其实KM ...

  4. 【文文殿下】浅谈KMP算法next数组与循环节的关系

    KMP算法 KMP算法是一种字符串匹配算法,他可以在O(n+m)的时间内求出一个模式串在另一个模式串下出现的次数. KMP算法是利用next数组进行自匹配,然后来进行匹配的. Next数组 Next数 ...

  5. 浅谈KMP算法——Chemist

    很久以前就学过KMP,不过一直没有深入理解只是背代码,今天总结一下KMP算法来加深印象. 一.KMP算法介绍 KMP解决的问题:给你两个字符串A和B(|A|=n,|B|=m,n>m),询问一个字 ...

  6. 浅谈 KMP 算法

    最近在复习数据结构,学到了 KMP 算法这一章,似乎又迷糊了,记得第一次学习这个算法时,老师在课堂上讲得唾沫横飞,十分有激情,而我们在下面听得一脸懵比,啥?这是个啥算法?啥玩意?再去看看书,完全听不懂 ...

  7. 浅谈分词算法(5)基于字的分词方法(bi-LSTM)

    目录 前言 目录 循环神经网络 基于LSTM的分词 Embedding 数据预处理 模型 如何添加用户词典 前言 很早便规划的浅谈分词算法,总共分为了五个部分,想聊聊自己在各种场景中使用到的分词方法做 ...

  8. 浅谈分词算法(4)基于字的分词方法(CRF)

    目录 前言 目录 条件随机场(conditional random field CRF) 核心点 线性链条件随机场 简化形式 CRF分词 CRF VS HMM 代码实现 训练代码 实验结果 参考文献 ...

  9. 浅谈分词算法(3)基于字的分词方法(HMM)

    目录 前言 目录 隐马尔可夫模型(Hidden Markov Model,HMM) HMM分词 两个假设 Viterbi算法 代码实现 实现效果 完整代码 参考文献 前言 在浅谈分词算法(1)分词中的 ...

随机推荐

  1. 【转】WPF自定义控件与样式(7)-列表控件DataGrid与ListView自定义样式

    一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等. 本文主要内容: DataGrid自定义样式: ListView自定义样式: 二.Dat ...

  2. matlab中如何将视频保存成图像

    利用MATLAB将视频的每一帧保存成一幅图像,并自动命名.本文方法简单,容易学习. 首先,读入视频.代码如下: mov = VideoReader('xxxxxx.avi'); % 将xxxxxx.a ...

  3. H3C S5120-52P-WiNet交换机配置

    配置console口登录验证密码 <H3C>system-view [H3C]user-interface aux 0 [H3C-ui-aux0]authentication-mode p ...

  4. .NET解决[Serializable] Attribute引发的Json序列化k_BackingField

    在WebAPI中的WebApiConfig直接加入如下配置 有问题找谷歌

  5. js实现选中文字 分享功能

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  6. Log4j2在项目中的常用配置

    ~~~~~~~~~~~~~~~~~~~~log4j2.xml文件内容 <?xml version="1.0" encoding="UTF-8"?>& ...

  7. 大杂烩 -- 查找单向链表倒数第m个元素

    基础大杂烩 -- 目录 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 1.输入并查找 方案:头插法,正向查找第m个元素. ...

  8. css3整理--box-sizing

    box-sizing语法: box-sizing : content-box || border-box || inherit 参数取值: content-box:此值为其默认值,其让元素维持W3C的 ...

  9. [转]复制、移动和删除:cp, rm, mv

    转自:http://www.cnblogs.com/benio/archive/2010/07/27/1785929.html 要复制文件,请使用cp(copy)命令.不过,cp命令的用途很多.除了单 ...

  10. C - 取石子游戏

    1堆石子有n个,两人轮流取.先取者第1次可以取任意多个,但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍.取完者胜.先取者负输出"Second win".先取者胜输出&q ...