[模板]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,此时k==len2=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=3==len2,执行操作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;
}

原文地址:https://www.cnblogs.com/--BLUESKY007/p/9349409.html

时间: 2024-08-01 07:32:20

[模板]KMP算法的相关文章

KMP算法的定义及KMP练手题 HDU 1711 Number Sequence (我的模板代码)

题意:就是要你来找出b数组在a数组中最先匹配的位置,如果没有则输出-1 思路:直接KMP算法(算法具体思想这位牛写的不错http://blog.csdn.net/v_july_v/article/details/7041827) AC代码: #include<cstdio> #include<cstring> #include<stdlib.h> #include<iostream> using namespace std; #define maxn 100

hdu 1711 KMP算法模板题

题意:给你两个串,问你第二个串是从第一个串的什么位置開始全然匹配的? kmp裸题,复杂度O(n+m). 当一个字符串以0为起始下标时.next[i]能够描写叙述为"不为自身的最大首尾反复子串长度". 当发生失配的情况下,j的新值next[j]取决于模式串中T[0 ~ j-1]中前缀和后缀相等部分的长度, 而且next[j]恰好等于这个最大长度. 防止超时.注意一些细节.. 另外:尽量少用strlen.变量记录下来使用比較好,用字符数组而不用string //KMP算法模板题 //hdu

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

功能:输入一个原串,再输入N个待匹配串,在待匹配串中找出全部原串的起始位置 原理:KMP算法,其实这个东西已经包含了AC自动机的思想(fail指针/数组),只不过适用于单模板匹配,不过值得一提的是在单模板大量匹配待匹配串时,这个会有相当大的优势,AC自动机虽然好想一些,但是在这一类问题上的性价比就略低了 1 var 2 i,j,k,l,m,n:longint; 3 a:array[0..100000] of longint; 4 s1,s2:ansistring; 5 begin 6 readl

【模板】KMP算法

KMP算法用于字符串匹配 1 /*KMP*/ 2 #include<stdio.h> 3 #include<string.h> 4 char s1[1000005],s2[1005];//s1 为待匹配串,s2为模板串 5 int nxt[1005],n,m; 6 int main() 7 { 8 scanf("%s%s",s1+1,s2+1); 9 n = strlen(s1+1); m = strlen(s2+1); 10 nxt[1] = 0; int i

拓展KMP算法 入门+模板

拓展KMP算法入门 博客推荐 扩展KMP算法, 图很形象,代码写的也很清晰,下面的模板就是出自该博客文章. 拓展KMP是求母串S长度为n和子串T长度为m,求S的每一个后缀子串与T的前缀子串匹配的最长长度. 代码实现 //求解模式串T的next数组,这个函数和下面的函数几乎相同 void getnext(string &T, int m, int[] next) { int a = 0, p = 0; next[0] = m; //T字符串自身和自身匹配 for(int i=1; i<m; i

kmp算法模板及理解

kmp算法是复杂度为O(n+m)的字符串匹配算法; 首先kmp算法的核心是在模式串中获得next数组,这个数组表示模式串的子串的前缀和后缀相同的最长长度; 这样在匹配的过程中如果指到不匹配的位置,模式串用next数组进行跳转到符合的位置,而目标串不需要再往回匹配,为什么是最长的相同的前缀后后缀呢? 因为只有这样才能一边避免可能漏掉的位置,一边尽量不重复已经匹配的位置; getNext的函数: void getNext() { int k = -1,j = 0,len = strlen(str);

【kmp算法】【Rabin-Karp算法】[BeiJing2011]矩阵模板

算法就不说了,反正是基于字符串匹配的.这里比较一下kmp和Rabin-Karp算法. <法一>kmp算法. 592788 lizitong 2462 Accepted 4828 kb 680 ms C++/Edit 2349 B 2014-03-29 19:07:02 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,m,a,b,q; char

KMP hihoCoder1015 KMP算法

人太蠢,,看了一天的KMP.. 刚开始看训练指南的,,后来才惊奇的发现原来刘汝佳写的f数组并不是Next数组! 总觉得和之前看过的完全不一样... 后来又百度了一下KMP,研究了很久,然后用自己的逻辑写了一份 http://blog.chinaunix.net/uid-23767307-id-5033555.html 这个人把KMP大篇幅的讲了,,大家可以看看.. 个人认为只要能理解Next数组的意义后,写出KMP算法就不是很难了 然而自己语文也不好就不多做解释了,,直接贴下我的模板好了 #in

数据结构--KMP算法总结

数据结构—KMP KMP算法用于解决两个字符串匹配的问题,但更多的时候用到的是next数组的含义,用到next数组的时候,大多是题目跟前后缀有关的 . 首先介绍KMP算法:(假定next数组已经学会,后边next数组会在介绍) 上图T为主链,P为模板链,要求P在T中是否出现,出现就返回位置. 朴素算法会顺序遍历,比较第一次的时候p[0]处失配,然后向后移动继续匹配.数据量大的时候这么做肯定是不可行的.所以这里就会有KMP算法!在一次失配之后,KMP算法认为这里已经失配了,就不能在比较一遍了,而是