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\n",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\n",Ans-len_m+1);
        }
        else
            printf("%d\n",Ans);
    }
}


posted @ 2019-04-16 20:21  SEC.VIP_网络安全服务  阅读(219)  评论(0编辑  收藏  举报