[模板]KMP算法
昨天晚上一直在调KMP(模板传送门),因为先学了hash[关于hash的内容会在随后进行更(gu)新(gu)]于是想从1开始读。。。结果写出来之后一直死循环,最后我还是改回从0读入字符串了。
[预先定义被匹配文本串为s1,长度为m;匹配模式串为s2,长度为n]
KMP算法在字符串匹配算法中时间复杂度比较优,可以做到在O(m+n)的时间内匹配,相对于无脑暴力匹配的O(m*n)复杂度而言要优很多。
KMP算法的思路比较简单,即在匹配前对字符串进行预处理,用空间换时间,通过处理next数组来实现在部分失配后的快速再匹配(从前缀中与已匹配部分的后一个字符开始继续匹配),从而避免不必要的重复检验、节省时间。
预处理next数组在KMP算法中发挥着非常重要的作用,也是KMP算法中比较难理解的部分,下面用一张图来解释预处理的原理和方式:
对于需要预处理的模式串s2(以“ABABCBABC”为例),从s2[1]开始(k=next[1]=0),发现s2[1]!=s2[0]失配(红色标注),执行操作A3,令next[1+1]=next[2]=0;对于s2[2] (k=next[2]=0),发现s2[2]s2[0]匹配(绿色标注),执行操作A2使next[3]=0+1=1;对于s2[3] (k=next[3]=1),s2[3]s2[1],执行操作A2使next[3+1]=1+1=2;s2[4] (k=next[4]=2)!=s2[2],执行操作A1(k=next[k]=next[2]=0),s2[4]!=s2[0],执行操作A3,next[4+1]=0;s2[5] (k=next[5]=0)!=s2[0],执行操作A3,next[5+1]=0;s2[6] (k=next[6]=0)s2[0],执行操作A2,next[6+1]=0+1=1;s2[7] (k=next[7]=1)s2[1],执行操作A2,next[7+1]=1+1=2;s2[8] (k=next[8]=2)!=s2[2],执行操作A1,(k=next[k]=next[2]=0),s2[8]!=s2[0],执行操作A3,next[8+1]=0。
至此next[]已完成预处理,next[]={0,0,0,1,2,0,0,1,2,0}(时间复杂度O(n))。
在完成了预处理next数组的操作后便需要做最后的匹配工作,同样配图来解释:
对于s1和s2的匹配(以"ABABABC"和“ABA”为例,以相同方式处理得next[]={0,0,0,1}),从s1[0]开始(k=0,cnt=0),s2[0]s1[0],执行操作B2,k=0+1=1;s1[1] (k=1,cnt=0)s2[1],执行操作B2,k=1+1=2;s1[2] (k=2,cnt=0)s2[2],执行操作B2,k=2+1=3,此时klen2=3,执行操作B3,cnt=0+1=1,ans[1]=2-3+2=1;s1[3] (k=3,cnt=1)!=s2[3] (s2[3]为空),执行操作B1,k=next[k]=next[3]=1,s1[3]s2[1],执行操作B2,k=1+1=2,;s1[4] (k=2,cnt=1)s2[2],执行操作B2,k=2+1=3len2,执行操作B3,cnt=1+1=2,ans[2]=4-3+2=3;s1[5] (k=3,cnt=2)!=s2[3] (s2[3]为空),执行操作B1,k=next[k]=next[3]=1,s1[5]s2[1],执行操作B2,k=1+1=2;s1[6] (k=2,cnt=2)!=s2[2],执行操作B1,k=next[k]=next[2]=0,s1[6]!=s2[0],不再执行操作,循环结束。
至此得到ans[]={0,1,3},cnt=2,匹配完成(时间复杂度O(m))。
以上就是本蒟蒻对KMP算法的个人理解,如果看了以上的叙述仍然不能理解KMP算法的实现可以考虑结合代码进行手动的模拟,具体实现代码如下:
#include<cstdio>//P3375
#include<iostream>
#include<algorithm>
#include<set>
#include<vector>
#include<queue>
#include<string>
#include<cstring>
#include<cstdlib>
#include<cmath>
const int L=1000010;
using namespace std;
char s1[L],s2[L];
int nxt[L],ans[L],cnt;
void getnext(){
int len=strlen(s2),k;
for(int i=1;i<len;i++){//如果从i=0开始会导致k=0->next[k]=0死循环
k=nxt[i];
while(k&&s2[i]!=s2[k]){//失配则回到前面能继续匹配的子串继续匹配(操作A1)
k=nxt[k];
}
if(s2[k]==s2[i]){//可以匹配时改变next[i+1]值(操作A2)
nxt[i+1]=k+1;
}
else{//(操作A3)
nxt[i+1]=0;//无法继续匹配next改为0
}
}
}
void KMP(){
int len1=strlen(s1),len2=strlen(s2);
getnext();
int k=0;
for(int i=0;i<len1;i++){
while(k&&s2[k]!=s1[i]){//部分匹配后失配前跳(操作B1)
k=nxt[k];
}
if(s2[k]==s1[i]){//完成匹配对k后移准备检验下一位匹配(操作B2)
k++;
}
if(k==len2){//完成完全匹配记录匹配位置(操作B3)
ans[++cnt]=i-len2+2;
}
}
}
int main(){
scanf("%s",s1);
scanf("%s",s2);
KMP();
int len2=strlen(s2);
for(int i=1;i<=cnt;i++){
printf("%d\n",ans[i]);
}
for(int i=1;i<=len2;i++){
printf("%d ",nxt[i]);
}
return 0;
}
[模板]KMP算法的更多相关文章
- [模板] KMP算法/Border
KMP 算法 KMP (Knuth-Morris-Pratt) 算法是一种在线性时间内匹配文本串和模式串的算法. 称字符串的 Border 集合为 \[ \operatorname {Border} ...
- 算法模板——KMP字符串匹配
功能:输入一个原串,再输入N个待匹配串,在待匹配串中找出全部原串的起始位置 原理:KMP算法,其实这个东西已经包含了AC自动机的思想(fail指针/数组),只不过适用于单模板匹配,不过值得一提的是在单 ...
- Luogu 3375 【模板】KMP字符串匹配(KMP算法)
Luogu 3375 [模板]KMP字符串匹配(KMP算法) Description 如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置. 为了减少骗分的情况,接下来 ...
- KMP算法自我理解 和 模板
字符串 abcd abc abcd abc 匹配串 cdabcd 匹配串的 next 0 0 0 0 1 2: 开始匹配 abcd abc abcd abc cd abc d a,d 匹配失 ...
- KMP算法(——模板习题与总结)
KMP算法是一种改进的模式匹配算法,相比于朴素的模式匹配算法效率更高.下面讲解KMP算法的基本思想与实现. 先来看一下朴素模式匹配算法的基本思想与实现. 朴素模式匹配算法的基本思想是匹配过程中如果该位 ...
- KMP算法模板&&扩展
很不错的学习链接:https://blog.csdn.net/v_july_v/article/details/7041827 具体思路就看上面的链接就行了,这里只放几个常用的模板 问题描述: 给出字 ...
- hdu 1711 KMP算法模板题
题意:给你两个串,问你第二个串是从第一个串的什么位置開始全然匹配的? kmp裸题,复杂度O(n+m). 当一个字符串以0为起始下标时.next[i]能够描写叙述为"不为自身的最大首尾反复子串 ...
- KMP算法(推导方法及模板)
介绍 克努斯-莫里斯-普拉特算法Knuth-Morris-Pratt字符串查找算法(简称为KMP算法)可在一个主文本字符串S内查找一个词W的出现位置.此算法通过运用对这个词在不匹配时本身就包含足够的信 ...
- KMP算法解题模板(更新)
/* kmp算法的主要作用在于对next数组的运用,所以这里只给出next数组的模板 性质1:对于每一个长度len的子串,该子串的最小循环节为len-next[len] 性质2:kmp的next不断向 ...
随机推荐
- ES使用C#添加和更新文档
ElasticSearch 使用C#添加和更新文档 这是ElasticSearch 2.4 版本系列的第四篇: 第一篇:ES1:Windows下安装ElasticSearch 第二篇:ES2:Elas ...
- Django之ORM操作(聚合 分组、F Q)
Django之ORM操作(聚合 分组.F Q) 聚合 aggregate()是QuerySet的一个终止子句,也就是说,他返回一个包含一些键值对的字典,在它的后面不可以再进行点(.)操作. 键的名 ...
- Java参数是值传递还是引用传递?
先来看看参数是如何传递的. 一.参数的传递 1.基本类型的参数传递 public static void main(String[] args) { int a = 1; fun(a); } priv ...
- shell脚本--操作MySQL数据库
其实就是一个很简单的套路,和其他语言差不多,首先连接数据库,然后在进行其他操作. 套路如下: #!/bin/bash mysql="mysql -uroot -proot" #连接 ...
- PAT L3-010 是否完全二叉搜索树
https://pintia.cn/problem-sets/994805046380707840/problems/994805049870368768 将一系列给定数字顺序插入一个初始为空的二叉搜 ...
- java 获取下一个字母(传大写返回大写,传小写返回小写)
public static String getNextUpEn(String en){ char lastE = 'a'; char st = en.toCharArray()[0]; if(Cha ...
- 快速理解Git结构
git pull:拉取远程服务器最新代码到本地(会自动merge) git add:将本地代码添加到暂存区 git commit:将暂存区的所有内容提交到当前分支(git会自动为我们创建第一个分支 ...
- 2 Modals of necessity
1 情况动词must 和词组have to, need to ,have got to 都表示必须做某事或者要求做某事. need to have to must have got to When ...
- Linux 查找文件命令 find whereis locate
Linux 有三个查找文件的命令:find, whereis, locate 其中find 不常用,whereis与locate经常使用,因为find命令速度较慢,因为whereis与locate是利 ...
- [转帖]漫画趣解Linux内核
漫画趣解Linux内核 https://blog.csdn.net/juS3Ve/article/details/84207142 Linux 内核漫画 今天,我来为大家解读一幅来自 TurnOff. ...