zoukankan      html  css  js  c++  java
  • [模板]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
    ",ans[i]);
        }
        for(int i=1;i<=len2;i++){
            printf("%d ",nxt[i]);
        }
        return 0;
    }
    
  • 相关阅读:
    员工看公司
    Java多线程
    Intellij热部署插件JRebel的详细配置及图解
    Java并发处理锁 Lock
    Java8 Stream
    Eclipse下Maven安装和配置
    IntelliJ IDEA 部署Tomcat及创建一个web工程
    IntelliJ IDEA 下搭建vue项目工程
    Vue Router路由管理器介绍
    用WebStorm搭建vue项目
  • 原文地址:https://www.cnblogs.com/--BLUESKY007/p/9349409.html
Copyright © 2011-2022 走看看