• KMP算法总结


    KMP算法总结

    ps:蒟蒻写的不太好,推荐神犇写的一篇有关KMP的博客,非常清楚,有动图匹配全过程,看一遍就懂,戳我

    本篇博客中的字符串都是从1开始的,后面不再解释

    用途

    KMP算法主要用于快速字符串匹配的,假设我们要在是字符串(s1)中找一个子串(s2),朴素的字符串匹配是枚举(s2)(s1)中的位置,然后一个个比较,直到出现不同或者全部匹配为止,这个时间复杂度最坏是(n^2)的,效率太低,于是,就有了KMP,可以线性时间复杂度匹配子串。

    一些概念

    对于一个字符串(s),我们用(f[i])记录这个子串前(i)个字符的最长的前缀和后缀相同的长度。(前缀不包括最后一个字符,后缀不包括第一个字符)
    例如对于字符串(ababa)
    第一个字符为(a),所以最长的前缀和后缀相同的长度为0,即(f[1]=0),
    前两个字符为(ab),(f[2]=0),
    前两个字符为(aba),最长前缀和后缀为(a),(不是(aba)),所以(f[3]=1),
    前两个字符为(abab),最长前缀和后缀为(ab),所以(f[4]=2),
    前两个字符为(ababa),最长前缀和后缀为(aba),(不是(ababa)),所以(f[5]=3),
    其实这个例子不够经典,不具有代表性,但也应该解释清楚了。

    基本思想

    对于朴素的匹配算法,我们会发现,在匹配过程中,一但出现不匹配的字符,我们前面的匹配信息就全部舍弃,这样浪费了大量的时间,但是这个信息是可以利用起来的。假设我们在(s1)中匹配到了第(i)位,在(s2)中已经匹配了(j)位,第(i)于第(j)位是不匹配的,那么我们会发现(s2)的前(j-1)个字符的最长前缀和最长后缀就已经匹配了,也就是前面的(f[j-1])位就已经匹配了,也就是(s1)的第(i-f[j-1])到第(i-1)位与(s2)的第(1)(f[j-1])位已经匹配了,此时我们只需要把(j)修改为(f[j-1]+1)就可以继续匹配了,而每当(j=m且a[i]=b[j])时,就是一次成功匹配。
    注意特判(j=1)(a[i] e b[j])时应该直接将(i)往后移一位。
    由于(s1)的每一个字符最多被访问一次,所以匹配的时间复杂度为O(n).

    for(int i=1,j=1;i<=n;++i,++j){
            if(a[i]!=b[j]){if(j!=1)j=f[j-1],--i;else j--;continue;}
            if(j==m)printf("%d
    ",i-j+1),j=f[m];
        }
    

    (f)数组的预处理

    匹配已经讲完了,现在我们要做的就是先预处理(f)数组了。
    这个其实就是不断的用串(s2)自己匹配自己。具体是怎样呢?直接看吧,手玩一下。

    for(int i=2;i<=m;++i){
            int j=i-1;
            while(b[f[j]+1]!=b[i]&&j>0)j=f[j];
            if(b[f[j]+1]==b[i])f[i]=f[j]+1;
            else f[i]=0;
        }
    

    整个KMP算法就这么多,非常好写,不过一般不会单独使用。

    完整代码(题目来源洛谷P3375【模板】KMP字符串匹配)

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e6+20;
    char a[N],b[N];
    int f[N];
    int main(){
        scanf("%s%s",a+1,b+1);
        int n=strlen(a+1);
        int m=strlen(b+1);
        for(int i=2;i<=m;++i){
            int j=i-1;
            while(b[f[j]+1]!=b[i]&&j>0)j=f[j];
            if(b[f[j]+1]==b[i])f[i]=f[j]+1;
            else f[i]=0;
        }
        for(int i=1,j=1;i<=n;++i,++j){
            if(a[i]!=b[j]){if(j!=1)j=f[j-1],--i;else j--;continue;}
            if(j==m)printf("%d
    ",i-j+1),j=f[m];
        }
        for(int i=1;i<=m;++i){
            printf("%d ",f[i]);
        }
    	cout<<endl;
        return 0;
    }
    
    
  • 相关阅读:
    053364
    053363
    oracle导出批量表N行记录
    053362
    053361
    053360
    053359
    053358
    053357
    053356
  • 原文地址:https://www.cnblogs.com/ljq-despair/p/8665917.html
Copyright © 2020-2023  润新知