KMP算法详解:

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt(雾)提出的。

对于字符串匹配问题(such as 问你在abababb中有多少个ab串?),朴素的想法是定一个i,从字符串首扫到字符串尾部来枚举字符串位置,找到一个首字符相同的就通过第二层for循环来继续往下一个字符一个字符的匹配。

直到匹配到长度和需要匹配的子串(模式串)长度相等,我们就说找到了一个在原串中的子串并将答案加一,然后继续往下像蜗牛一样的搜索。

有关相似的算法,链接这里hash

举个栗子:A:GCAKIOI      B:GC     ,那么我们称B串是A串的子串

我们称等待匹配的A串为主串,用来匹配的B串为模式串。

一般的朴素做法就是枚举B串的第一个字母在A串中出现的位置并判断是否适合,而这种做法的时间复杂度是O(mn)的,当你处理一篇较长文章的时候显然就会超时。

我们会发现在字符串匹配的过程中,绝大多数的尝试都会失败,那么有没有一种算法能够利用这些失败的信息呢?

KMP算法就是

KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的

设主串(以下称为T)

设模式串(以下称为W)

用暴力算法匹配字符串过程中,我们会把T[0] 跟 W[0] 匹配,如果相同则匹配下一个字符,直到出现不相同的情况,此时我们会丢弃前面的匹配信息,然后把T[1] 跟 W[0]匹配,循环进行,直到主串结束,或者出现匹配成功的情况。这种丢弃前面的匹配信息的方法,极大地降低了匹配效率。

我们来看一看KMP是怎么工作的

在KMP算法中,对于每一个模式串我们会事先计算出模式串的内部匹配信息(也就是说这个东西只和模式串有关,可以预处理,这个处理我们后面会提到),在匹配失败时最大的移动模式串,以减少匹配次数。

比如,在简单的一次匹配失败后,我们会想将模式串尽量的右移和主串进行匹配。右移的距离在KMP算法中是如此计算的:在已经匹配的模式串子串中,找出最长的相同的前缀和后缀,然后移动使它们重叠。

我们用两个指针i和j分别表示A[i-j+1......i]和B[1......j]完全相等,也就是说i是不断增加的,并且随着i的增加,j也相应的变化,并且j满足以A[j]结尾的长度为j的字符串正好匹配B串的前j个字符,现在需要看A[i+1]和B[j+1]的关系

  • 当A[i+1]=B[j+1]时,我们将i和j各增加1
  • 否则,我们减小j的值,使得A[i-j+1......i]和B[1......j]保持匹配并尝试匹配新的A[i+1]和B[j+1]

举个栗子:

T: a b a b a b a a b a b a c b

W:a b a b a c b

当i=j=5时,此时T[6]!=W[6],这表明此时j不能等于5了,这个时候我们要改变j的值,使得W[1...j]中的前j'个字母与后j'个字母相同,因为这样j变成j'后(也就是将W右移j'个长度)才能继续保持i和j的性质。这个j'显然越大越好。在这里W[1...5]是匹配的,我们发现当ababa的前三个字母和后三个字母都是aba,所以j'最大也就是3,此时情况是这样

T: a b a b a b a a b a b a c b

W:      a b a b a c b

那么此时i=5,j=3,我们又发现T[6]与W[4]是相等的,然后T[7]与W[5]是相等的(这里是两步)

所以现在是这种情况:i=7,j=5

T: a b a b a b a a b a b a c b

W:      a b a b a c b

这个时候又出现了T[8]!=W[6]的情况,于是我们继续操作。由于刚才已经求出来了当j=5时,j'=3,所以我们就可以直接用了(通过这里我们也可以发现j'是多少和主串没有什么关系,只和模式串有关系)

于是又变成了这样

T: a b a b a b a a b a b a c b

W:            a b a b a c b

这时,新的j=3依然不能满足A[i+1]=B[j+1],所以我们还需要取j'

我们发现当j=3时aba的第一个字母和最后一个字母都是a,所以这时j'=1

新的情况:

T: a b a b a b a a b a b a c b

W:                  a b a b a c b

仍然不满足,这样的话j需要减小到j'就是0(我们规定当j=1时,j'=0)

T: a b a b a b a a b a b a c b

W:                     a b a b a c b

终于,T[8]=B[1],i变为8,j变为1,我们一位一位往后,发现都是相等的,最后当j=7还满足条件时,我们就可以下结论:W是T的子串,并且还可以找到子串在主串中的位置(i+1-m+1,因为下标从0开始)

这一部分的代码其实很短,因为用了for循环

inline void kmp()
{int j=0;for(int i=0;i<n;i++){while(j>0&&b[j+1]!=a[i+1]) j=nxt[j];if(b[j+1]==a[i+1]) j++;if(j==m) {printf("%d\n",i+1-m+1);j=nxt[j]; //当输出第一个位置时 直接break掉 //当输出所有位置时 j=nxt[j]; //当输出区间不重叠的位置时 j=0
        }}
}

这里就有一个问题:为什么时间复杂度是线性的?

我们从上述的j值入手,因为每执行一次while循环都会使j值减小(但不能到负数),之后j最多+1,因此整个过程中最多加了n个1.于是j最多只有n个机会减小。这告诉我们,while循环最多执行了n次,时间复杂度平摊到for循环上后,一次for循环的复杂度是O(1),那么总的时间复杂度就是O(n)的(n是主串长度)。这样的分析对于下文的预处理来说同样有效,也可以得到预处理的时间复杂度是O(m)(m是模式串长度)

接下来是预处理

预处理并不需要按照定义写成O(m2)甚至O(m3),窝们可以通过nxt[1],nxt[2]....nxt[n-1]来求得nxt[n]的值

举个栗子

W :a b a b a c b

nxt:0 0 1 2 ??

假如我们有一个串,并且已经知道了nxt[1~4]那么如何求nxt[5]和nxt[6]呢?

我们发现,由于nxt[4]=2,所以w[1~2]=w[3~4],求nxt[5]的时候,我们发现w[3]=w[5],也就是说我们可以在原来的基础上+1,从而得到更长的相同前后缀,此时nxt[5]=nxt[4]+1=3

W :a b a b a c b

nxt:0 0 1 2 3?

那么nxt[6]是否也是nxt[5]+1呢?显然不是,因为w[nxt[5]+1]!=w[6],那么此时我们可以考虑退一步,看看nxt[6]是否可以由nxe[5]的情况所包含的子串得到,即是否nxt[6]=nxt[nxt[5]]+1?

事实上,这样一直推下去也不行,于是我们知道nxt[6]=0

那么预处理的代码就是这样的

inline void pre()
{nxt[1]=0;//定义nxt[1]=0 int j=0;rep(i,1,m-1){while(j>0&&b[j+1]!=b[i+1]) j=nxt[j];//不能继续匹配并且j还没有减到0,就退一步 if(b[j+1]==b[i+1]) j++;//如果能匹配,就j++ nxt[i+1]=j;//给下一个赋值
    }
}

全部代码:

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
char s1[1000003],s2[1000003];
int p[1000002];
int n,m;
inline void yuchuli()
{int j=0;for(int i=1;i<m;i++){while(j>0&&s2[i+1]!=s2[j+1])j=p[j];if(s2[i+1]==s2[j+1])j++;p[i+1]=j;}
}
int main(){scanf("%s",s1+1);scanf("%s",s2+1);n=strlen(s1+1);m=strlen(s2+1);yuchuli();int j=0;for(int i=0;i<n;i++){while(j>0&&s1[i+1]!=s2[j+1])j=p[j];if(s2[j+1]==s1[i+1])j++;if(j==m){printf("%d\n",i+2-j);j=p[j];}}for(int i=1;i<=m;i++){printf("%d ",p[i]);}return 0;
}

完结!

部分(后半段)内容引用ych大佬的

原因链接

转载于:https://www.cnblogs.com/lbssxz/p/11093696.html

KMP算法详解P3375 【模板】KMP字符串匹配题解相关推荐

  1. kmp算法详解php,php中字符串匹配KMP算法实现例子

    KMP算法是一个比较高级的算法了,加了改进了,下面我们来在php中实现KMP算法,希望例子对各位同学会带来帮助哦. kmp算法是一种改进的字符串匹配算法,由D.E.Knuth与V.R.Pratt和J. ...

  2. 【KMP算法详解——适合初学KMP算法的朋友】

    相信很多人(包括自己)初识KMP算法的时候始终是丈二和尚摸不着头脑,要么完全不知所云,要么看不懂书上的解释,要么自己觉得好像心里了解KMP算法的意思,却说不出个究竟,所谓知其然不知其所以然是也. 经过 ...

  3. KMP算法详解及代码

    KMP算法详解及代码 KMP算法详解及代码 定义及应用 理论 基本概念 next 数组 总结 注意 代码 KMP算法详解及代码 最近正好在看字符串相关的算法内容,就顺便把KMP算法回顾了一下.相应的代 ...

  4. 字符串匹配之KMP算法详解

    kmp算法又称"看毛片"算法,是一个效率非常高的字符串匹配算法.不过由于其难以理解,所以在很长的一段时间内一直没有搞懂.虽然网上有很多资料,但是鲜见好的博客能简单明了地将其讲清楚. ...

  5. KMP算法详解及各种应用

    KMP算法详解: KMP算法之所以叫做KMP算法是因为这个算法是由三个人共同提出来的,就取三个人名字的首字母作为该算法的名字.其实KMP算法与BF算法的区别就在于KMP算法巧妙的消除了指针i的回溯问题 ...

  6. 奇淫巧技的KMP算法--详解

    奇淫巧技的KMP算法–详解 花了一下午时间,看了十几个博客,终于拿下了KMP高地,现在总结下下自己对KMP的理解和实现. 情景1 假如你是一名生物学家,现在,你的面前有两段 DNA 序列 S 和 T, ...

  7. BF与KMP算法详解

    日升时奋斗,日落时自省 目录 一.BF暴力算法 二.KMP算法 1.next数组 2.next数组优化(nextval) 一.BF暴力算法 暴力算法是普通的模式匹配算法 针对一个主串和一个子串,子串是 ...

  8. 【模式匹配】之 —— KMP算法详解及证明

    一    RevisionsHistory 1 一       Revisions History 二       前言 三       关于算法学习 四       KMP算法始末 KMP算法是用来 ...

  9. KMP 算法详解(CPP 实现)

    转载请标明出处:https://blog.csdn.net/kiss0tql/article/details/81416283 本文来自:deemo的博客 说明 kmp 算法思想 next 数组计算 ...

最新文章

  1. SAP 对HU做货物移动报错-Only 0 serial numbers entered instead of 30 -
  2. Linux下使用popen()执行shell命令
  3. comsol积分函数_怎样在COMSOL中实现时间和空间积分
  4. Ajax — 图书管理
  5. 2016国产开源软件Top100(Q1)
  6. [css] 颜色hsla的字母分别表示什么?
  7. tensorrt轻松部署高性能dnn推理_NVIDIA TensorRT高性能深度学习推理
  8. Rabbitmq学习笔记007---Centos7下安装rabbitmq_测试通过
  9. 手把手系列|风控建模中共线性的影响和处理(上)
  10. 烂漫的青春后期 35岁前必须做好十件事情[转载]
  11. 【智库解读】明天系、安邦系、海航系、复星系、联想系、中植系、三胞系、宝能系等27个超级民营金融巨头名单及其持股图揭秘!
  12. SolidWorks to URDF转换及常见机器人模型
  13. linux r的数据是存在,R语言通过loess去除某个变量对数据的影响
  14. 推动前端团队基础设施建设流程方案计划书
  15. 汇编语言 | CALL 和RET指令
  16. 怎么将CAD图纸转换成高清晰度PNG格式怎么操作?
  17. 从SUMSUNG闪存芯片编号识容量
  18. python输出一个函数多项式_python实现PolynomialFeatures多项式的方法
  19. 【CV】ShuffleNet V2:高效 CNN 架构设计的实用指南
  20. 物联网芯片大盘点:产业规模及全球芯片供应商一览

热门文章

  1. vue element container 子路由
  2. SpringBoot 工程目录 整合mybatis-mysql(注解类型)
  3. pytorch torch.utils.data.TensorDataset
  4. docker 与宿主共享文件
  5. nginx ngx_http_core_module(http server location)
  6. 1.1 为什么是 ML 策略
  7. C语言rand函数生成随机数详解和示例
  8. 可靠型园区网组网,用VRRP还是堆叠?
  9. 开机启动简易收集面板并最小化
  10. ZooKeeper学习总结(3)——ZooKeeper常见面试题