昨天晚上一直在调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算法的更多相关文章

  1. [模板] KMP算法/Border

    KMP 算法 KMP (Knuth-Morris-Pratt) 算法是一种在线性时间内匹配文本串和模式串的算法. 称字符串的 Border 集合为 \[ \operatorname {Border} ...

  2. 算法模板——KMP字符串匹配

    功能:输入一个原串,再输入N个待匹配串,在待匹配串中找出全部原串的起始位置 原理:KMP算法,其实这个东西已经包含了AC自动机的思想(fail指针/数组),只不过适用于单模板匹配,不过值得一提的是在单 ...

  3. Luogu 3375 【模板】KMP字符串匹配(KMP算法)

    Luogu 3375 [模板]KMP字符串匹配(KMP算法) Description 如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置. 为了减少骗分的情况,接下来 ...

  4. KMP算法自我理解 和 模板

    字符串   abcd abc abcd abc 匹配串   cdabcd 匹配串的 next  0 0 0 0 1 2: 开始匹配 abcd abc abcd abc cd abc d a,d 匹配失 ...

  5. KMP算法(——模板习题与总结)

    KMP算法是一种改进的模式匹配算法,相比于朴素的模式匹配算法效率更高.下面讲解KMP算法的基本思想与实现. 先来看一下朴素模式匹配算法的基本思想与实现. 朴素模式匹配算法的基本思想是匹配过程中如果该位 ...

  6. KMP算法模板&&扩展

    很不错的学习链接:https://blog.csdn.net/v_july_v/article/details/7041827 具体思路就看上面的链接就行了,这里只放几个常用的模板 问题描述: 给出字 ...

  7. hdu 1711 KMP算法模板题

    题意:给你两个串,问你第二个串是从第一个串的什么位置開始全然匹配的? kmp裸题,复杂度O(n+m). 当一个字符串以0为起始下标时.next[i]能够描写叙述为"不为自身的最大首尾反复子串 ...

  8. KMP算法(推导方法及模板)

    介绍 克努斯-莫里斯-普拉特算法Knuth-Morris-Pratt字符串查找算法(简称为KMP算法)可在一个主文本字符串S内查找一个词W的出现位置.此算法通过运用对这个词在不匹配时本身就包含足够的信 ...

  9. KMP算法解题模板(更新)

    /* kmp算法的主要作用在于对next数组的运用,所以这里只给出next数组的模板 性质1:对于每一个长度len的子串,该子串的最小循环节为len-next[len] 性质2:kmp的next不断向 ...

随机推荐

  1. 阿里云服务器使用镜像市场上的环境以后sql不能远程问题

    关于阿里云的服务器,首先要说的就是买了以后是没有环境的,什么都需要自己配置,也是在这个上面栽了很多跟头最后去的镜像市场买的一个IIS8+SQL2016的asp.net环境 怎么说呢,感觉有些问题的本源 ...

  2. 使用json读写文件中的数据

    把json的数据写入到文件中 import json with open('data.json','w+') as f: json.dump({"name":"张彪&qu ...

  3. Spring.profile配合Jenkins发布War包,实现开发、测试和生产环境的按需切换

    前两篇不错 Spring.profile实现开发.测试和生产环境的配置和切换 - Strugglion - 博客园https://www.cnblogs.com/strugglion/p/709102 ...

  4. 生成短链接的URL

    假设你想做一个像微博短链接那样的短链接服务,短链接服务生成的URL都非常短例如: http://t.cn/E70Piib, 我们应该都能想到链接中的E70Piib对应的就是存储长链接地址的数据记录的I ...

  5. vue传参

    <template> <ul> <li v-for="item in list" :key="item.id"> <b ...

  6. Arrays.copyOf() 和 System.arrayCopy()分析

    java数组的拷贝四种方法:for.clone.System.arraycopy.Arrays.copyof public class Test1 { public static void main( ...

  7. linux的一些基本命令

    一.linux的一些基本命令(使用的是CentOS7系统): 1.创建用户组,创建新用户并添加到用户组 添加用户,添加用户组命令: 增加用户:useradd -d /usr/username -m u ...

  8. win10远程桌面连接提示身份验证错误,要求的函数不受支持的解决方案

    转自https://www.baidu.com/link?url=67JXh4h79mN47mEenuH_ElGkSh9_GdOiY-Xp9Ihw0_mQIZHrPx-HxY3EIm_nTZKPoRZ ...

  9. C#中is运算符

    is运算符可以检查对象是否与特定的类型兼容.“兼容”表示对象或者该类型,或者派生自该类型.例如,要检查变量是否与object类型兼容,可以使用下面的代码: int i=10; if(i  is  ob ...

  10. Bootstrap之响应式导航栏

    代码: <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8 ...