zoukankan      html  css  js  c++  java
  • KMP & e-KMP详解

    感谢kuangbin巨巨的讲解

    KMP解决的是一个字符串匹配的问题
    什么是字符串的匹配问题呢?
    比如,字符串S “ABCDABCDABDE”,字符串P “ABCDABD”
    现在问P在S中出现了几次

    暴力计算 时间复杂度n*m
    在这里插入图片描述
    这个方法的时间复杂度太过于大了,有没有好一点的方法呢,可不可以每一次不要再从开头开始匹配呢
    在这里插入图片描述
    我们发现在P中一些字符是没有必要再比较的比如,我们可以下一次比较的时候直接从S[4]开始比较,这样就快了很多
    在这里插入图片描述
    那我们现在的问题就是如何求出这一次S数组应该从哪一位开始比较,我们引入数组 next[]
    假如现在s[i]!=p[j],则s[i-j,i-1]==p[0,j-1],我们现在需要移动字符串P k位,使s[i-k,i-1]==p[0,k-1],这个式子等价于p[0,k-1]==p[j-k,j-1]
    所以k的取值只和字符串P有关,而我们数组next中存的就是k,所以数组next只和p有关
    next数组:
    含义:next[k]为满足p[0,k-1]==p[j-k,j-1]的最大k值,即当前字符以前的字符串中有多长的相同前缀和后缀。
    在这里插入图片描述
    设置数组next代码如下:
    在这里插入图片描述
    对于next数组还有一些深入的理解,可能在做一些题的时候可以用到。
    1.沿着next数组向前或者向后,得到的字符串是此处的前缀和后缀字符串,比如
    在这里插入图片描述
    比如next[11]=8就代表着字母B之前的前缀字符串就是P[0-8]

    2.周期性字符串:if(n%(n-next[n])==0) 则循环节长度为n-next[n]
    在这里插入图片描述

    专题练习
    1.POJ - 3461
    ac代码

    #include <iostream>
    #include <cstdio>
    using namespace std;
    char T[1000005];
    char W[10005];
    int Next[10005];
    void kmp_pre(int a)
    {
        int i,j;
        j=Next[0]=-1;
        i=0;
        while (i<a) {
            while (j!=-1&&W[i]!=W[j]) {
                j=Next[j];
            }
            Next[++i]=++j;
        }
    }
    int main()
    {
        int t;
        scanf("%d",&t);
        while (t--) {
            scanf("%s%s",W,T);
            int a=strlen(W);
            int b=strlen(T);
            kmp_pre(a);
            int i,j,Ans;
            i=j=Ans=0;
            while (i<b&&j<a) {
                if (j==-1||W[j]==T[i]) {
                    i++;
                    j++;
                }
                else
                    j=Next[j];
                if (j==a) {
                    Ans++;
                    j=Next[j];
                }
            }
            printf("%d
    ",Ans);
        }
    }
    
    

    这个题比较基础,就是按着模版打就好了,第一次交没有过因为,将所有的strlen没有保存下来,每次都在while里重新计算导致原本O(n)变成了O(n^2)
    2.HDU-1711
    AC代码

    #include <iostream>
    #include <cstdio>
    using namespace std;
    int M[10004];
    int N[1000004];
    int Next[10004];
    void kmp_pre(int n)
    {
        int i,j;
        j=Next[0]=-1;
        i=0;
        while (i<n) {
            while (j!=-1&&M[i]!=M[j]) {
                j=Next[j];
            }
            Next[++i]=++j;
        }
    }
    int main()
    {
        int t;
        scanf("%d",&t);
        while (t--) {
            int len_m,len_n;
            scanf("%d%d",&len_n,&len_m);
            for (int i=0; i<len_n; i++) {
                scanf("%d",&N[i]);
            }
            for (int i=0; i<len_m; i++) {
                scanf("%d",&M[i]);
            }
            kmp_pre(len_m);
            int Ans=-1;
            int i=0,j=0;
            while (i<len_n&&j<len_m) {
                if (j==-1||N[i]==M[j]) {
                    i++;
                    j++;
                }
                else
                    j=Next[j];
                if (j==len_m) {
                    Ans=i;
                    break;
                }
            }
            if (Ans!=-1) {
                printf("%d
    ",Ans-len_m+1);
            }
            else
                printf("%d
    ",Ans);
        }
    }
    
    
    
  • 相关阅读:
    2019.9.4 二维树状数组
    2019.9.4 简单题
    0052-YH的计算器
    0051-打乱顺序的三位数
    0050-计算天数
    0049-学校的上网费
    0048-三角形的判断
    0047-月份转换
    0046-简单的分段函数(二)
    0045-简单的分段函数(一)
  • 原文地址:https://www.cnblogs.com/cnsec/p/11830673.html
Copyright © 2011-2022 走看看