KMP是一种字符串匹配算法,它在时间复杂度上较暴力匹配算法由很大的优势。比如我要找字符串S中是否存在子串P,如果暴力匹配的话,则时间复杂度为O(n*m),而kmp算法时间复杂度为O(n+m)

这里我们有一个辅助的数组next[](先别管怎么求出来的),next[i]含义是模式串P中[0....i-1]这一段的长度小于这段字符串的长度的最长公共前缀(比如ababa,公共前缀就是aba)。

好,那我们接下来讲一下kmp算法的具体操作:

假设,我们开始有字符串S:ababaaba   模式串P:abaa

对应next[i](0=<i<=len(P))的值为:

next[0]=-1 (无)

next[1]=0  (a)

next[2]=0  (ab)

next[3]=1  (aba)

next[4]=1  (abaa)

好,有了next数组,我们接下来进行匹配,设i=0是S上的当前匹配位置,j=0是P上的当前匹配位置。

第一次匹配,一直到i=3,j=3时匹配失败,令j=nxet[j]继续匹配。(为什么可以令j=next[j]?简单来说P[0...0]等于P[2...2],而通过第一次匹配,我们知道P[2..2]等于S[2...2],所以可以跳过这一段不用重复匹配,具体原理接下来解释)

第二次匹配,从i=3,j=1开始,匹配成功,获得答案。

大概过程就是这样。

下面按我自己的理解,解释一下kmp的原理:

如下图所示(图很丑,我真的不知道怎么画图),S[0...i]和P[0...i]匹配上了,匹配到i+1时匹配失败。

好,我们仔细分析一下,设L=nxet[i+1],则P[0...L]等于P[i-L...i],又因为通过刚才的匹配,我们确定了S[0...i]等于P[0...i],所以在S上也有一段对应的S[L-i...i]=P[L-i..i]=P[0...L]。

所以第二次匹配时,我们可以直接将P挪动,使P[0...L]对应S[L-i...i],直接从i+1开始匹配(即上文中的j=next[j]),如下图所示:

P[0...L]=S[L-i..i]可以理解,但是为什么可以直接挪过来呢,忽略了可能出现的情况怎么办?比如说下图这样的情况:

是否会有一段这样的字符串S[k..k+m]被我们忽略呢?若有的话,那显然k的位置更优因为i-k>L更有利于我们减少重复匹配。

实际上是不存在的,很容易知道,若存在一段长度大于L的S[k...i]=P[0...i-k+1]那么因为P[0...i]=S[0...i]肯定会有一段P[k..i]=S[k..i]=P[0..i-k+1],即P[0...i-k+1]和P[k..i]是一段公共前缀。

但是前面我们说了L=next[i+1]表示P[0...i]的最长公共前后缀,而上述的情况存在则说明有比L更长的公共前缀,这就矛盾了,所以S[k...k+m]这样的字符串是不存在的。

好了,这下kmp的原理我们知道了,接下来说next数组是如何构造的:

其实求next数组相当于模式串P自己跟自己做kmp,然后将最大的匹配结果记录在对应位置,所以实际上求next数组的代码跟kmp是几乎一样的。

模板代码:

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int M=1e6+; int nxt[M];
char s[M],p[M]; //获得next数组
void getnext(char *p,int len){
int i,j;
i=,j=nxt[]=-;
while(i<len){
while(j!=-&&p[i]!=p[j]) j=nxt[j];
nxt[++i]=++j;
}
}
//返回p在s中第一次出现的位置
void kmp(char *s,char *p){
int len1,len2,i,j;
i=j=;
len1=strlen(s);
len2=strlen(p);
getnext(p,len2);
while(i<len1){
while(j!=-&&s[i]!=p[j]) j=nxt[j];
i++,j++;
if(j==len2)
return i-len2+;
}
return -;
}

KMP常见题型:

一、字符串匹配,求出模式串P在S中是否存在,输出第一次出现的位置

HDU 1711

二、求模式串P在S中的出现次数(注意分可重和不可重的情况)

HDU 1686

三、求所有公共前后缀(既是前缀又是后缀)

POJ 2752

四、求字符串循环节

HDU 3746(找最小循环节)

FZU 1901(求所有循环节)

五、求所有S的前缀在S中出现次数之和

HDU 3336

六、最大最小表示

HDU 3374

KMP模板及总结的更多相关文章

  1. hdu 1686 KMP模板

    // hdu 1686 KMP模板 // 没啥好说的,KMP裸题,这里是MP模板 #include <cstdio> #include <iostream> #include ...

  2. Oulipo HDU 1686 KMP模板

    题目大意:求模式串在主串中的出现次数. 题目思路:KMP模板题 #include<iostream> #include<algorithm> #include<cstri ...

  3. KMP模板(bin)

    KMP模板 主要是kuangbin的模板,之后加了一点我的习惯和理解. kmpN() 作用:构造next数组 参数:模式串,模式串长度 kmpC() 作用:返回模式串在主串中出现的次数(可重复) 参数 ...

  4. HDU 1711 - Number Sequence - [KMP模板题]

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1711 Time Limit: 10000/5000 MS (Java/Others) Memory L ...

  5. HDU 1711 Number Sequence(KMP模板)

    http://acm.hdu.edu.cn/showproblem.php?pid=1711 这道题就是一个KMP模板. #include<iostream> #include<cs ...

  6. 剪花布条---hdu2087(kmp模板)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2087 kmp模板题: #include <cstdio> #include <cst ...

  7. Oulipo----poj3461(kmp模板)

    题目链接:http://poj.org/problem?id=3461 和 减花布条 的题对比一下: 求s2中s1的个数kmp模板: #include<stdio.h> #include& ...

  8. kmp模板 && 扩展kmp模板

    kmp模板: #include <bits/stdc++.h> #define PB push_back #define MP make_pair using namespace std; ...

  9. kuangbin专题16B(kmp模板)

    题目链接: https://vjudge.net/contest/70325#problem/B 题意: 输出模式串在主串中出现的次数 思路: kmp模板 在 kmp 函数中匹配成功计数加一, 再令 ...

  10. [HDU1711]KMP模板

    解题关键:1.直接套kmp模板即可,注意最后输出的位置,需要在索引的位置+1. 2.next用作数组名在oj中会编译错误, 3.选用g++,只有g++才会接受bits/stdc++.h OJ中g++和 ...

随机推荐

  1. 【费用流】【网络流24题】【P1251】 餐巾计划问题

    Description 一个餐厅在相继的 \(N\) 天里,每天需用的餐巾数不尽相同.假设第 \(i\) 天需要 \(r_i\)块餐巾.餐厅可以购买新的餐巾,每块餐巾的费用为 \(p\) 分;或者把旧 ...

  2. 团体程序设计天梯赛L3-019 代码排版(23分)

    打算学完编译原理后再次实现它... 以下为比较“杂乱”的方法: 海量数据: https://pan.baidu.com/s/1Prd0ZqNLoCLLvXyJjCef3w 如果大家有发现这个程序的问题 ...

  3. 梯度下降法求解函数极大值-Matlab

    目录 目录题目作答1. 建立函数文件ceshi.m2. 这是调用的命令,也可以写在.m文件里3. 输出结果题外话 题目 作答 本文使用MATLAB作答 1. 建立函数文件ceshi.m functio ...

  4. C#线程篇---解答线程之惑(2)

    我们都知道,在这个行业,追求的就是用最少的时间学最多的知识,这是我写这个系列最想达到的目标,在最快的时间内,帮助更多的人学习更多的线程知识. 前一篇,讲述了线程基础,给大家铺垫了一个基础,这一篇着重介 ...

  5. SIFT算法学习

    几个关于SIFT算法的blog,写的很好,链接学习一下 小北的家谈谈SIFT.PCA-SIFT.SURF及我的一点思考http://blog.csdn.net/ijuliet/article/deta ...

  6. SpringMVC之@RequestParam @RequestBody @RequestHeader 等详解

    转自:http://blog.csdn.net/kobejayandy/article/details/12690161?reload 简介: handler method 参数绑定常用的注解,我们根 ...

  7. Gulp API之怎样压缩CSS

    先做一个简单的科普 gulp.src() 是用来定位执行路径的,参数通常是一个path gulp.dest() 是用来定位输出路径的,执行的结果都会保存在这个路径下面,可以到路径下面查看结果 gulp ...

  8. javascript的未知尺寸图片保持比例水平垂直居中函数

    JavaScript的图片在容器内水平垂直居中的函数,利用图片加载获取图片大小,使之在父节点内水平垂直居中 展示方式有两种: 1.当参数keepImageFull为true:保持图片比例,使图片可完整 ...

  9. 程序员 & 设计师都能用上的 75 份速查手册

    分享75份开发人员和设计师会用到的速查手册,由 vikas 收集整理,包括:jQuery.HTML.HTML5.CSS.CSS3.JavaScript.Photoshop .git.Linux.Jav ...

  10. ASP.NET对无序列表批量操作的三种方法

    在网页开发中,经常要用到无序列表.事实上在符合W3C标准的div+css布局中,无序列表被大量使用,ASP.NET虽然内置了BulletedList控件,用于创建和操作无序列表,但感觉不太好用.本篇介 ...