[模板]KMP算法

时间:2022-06-05 02:48:33

昨天晚上一直在调KMP(模板传送门),因为先学了hash[关于hash的内容会在随后进行更(gu)新(gu)]于是想从1开始读。。。结果写出来之后一直死循环,最后我还是改回从0读入字符串了。

[预先定义被匹配文本串为s1,长度为m;匹配模式串为s2,长度为n]

KMP算法在字符串匹配算法中时间复杂度比较优,可以做到在O(m+n)的时间内匹配,相对于无脑暴力匹配的O(m*n)复杂度而言要优很多。

KMP算法的思路比较简单,即在匹配前对字符串进行预处理,用空间换时间,通过处理next数组来实现在部分失配后的快速再匹配(从前缀中与已匹配部分的后一个字符开始继续匹配),从而避免不必要的重复检验、节省时间。

预处理next数组在KMP算法中发挥着非常重要的作用,也是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数组的操作后便需要做最后的匹配工作,同样配图来解释:

[模板]KMP算法

对于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. &lbrack;模板&rsqb; KMP算法&sol;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算法模板&amp&semi;&amp&semi;扩展

    很不错的学习链接: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. centos6&period;5安装elasticsearch

    java下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.htmles下载地址 : ...

  2. 2&gt&semi;&amp&semi;1 linux

    2>&1使用 2>&1使用 一 相关知识 1)默认地,标准的输入为键盘,但是也可以来自文件或管道(pipe |).2)默认地,标准的输出为终端(terminal),但是也可 ...

  3. React Native工作小技巧及填坑记录

    以下是本人在React Native开发工作中使用的一些小技巧,记录一下. 1.从网络上拉取下来的React Native缺少React和React Native库. 终端 1. cd 项目根目录 2 ...

  4. LeetCode 350&period; Intersection of Two Arrays II

    Given two arrays, write a function to compute their intersection. Example:Given nums1 = [1, 2, 2, 1] ...

  5. Chrome开发,debug的使用方法。

    怎样打开Chrome的开发者工具? 你可以直接在页面上点击右键,然后选择审查元素: 或者在Chrome的工具中找到: 或者,你直接记住这个快捷方式: Ctrl+Shift+I (或者Ctrl+Shif ...

  6. Javascript中日期函数的相关操作

    Date对象具有多种构造函数,下面简单列举如下: new Date() new Date(milliseconds) new Date(datestring) new Date(year, month ...

  7. WPF 类型&OpenCurlyDoubleQuote;System&period;ComponentModel&period;ISupportInitialize”在未被引用的程序集中定义。

    问题:类型“System.ComponentModel.ISupportInitialize”在未被引用的程序集中定义.必须添加对程序集“System, Version=4.0.0.0, Cultur ...

  8. POJ 3669 广度优先搜索

    题意:巨大流星雨即将袭来.每个流星会对击中的地方以及周围(上下左右四格)造成破坏.Bessie开始时位于(0, 0)位置,并希望逃到一处不会被袭击到的地方(在第一象限内).已知每移动一格需要1个时间单 ...

  9. Greatest common divisor&lpar;gcd&rpar;

    欧几里得算法求最大公约数 If A = 0 then GCD(A,B)=B, since the GCD(0,B)=B, and we can stop. If B = 0 then GCD(A,B) ...

  10. UVALive 6467

    题目链接 : http://acm.sdibt.edu.cn/vjudge/contest/view.action?cid=2186#problem/C 题意:对于斐波那契数列,每个数都mod m , ...