论KMP

一、算法简介

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,

因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。

KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。

具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。时间复杂度O(m+n)。

(博主很懒所以扒了百度词条的简介QAQ

二、算法原理&流程

首先我们对于模式串和主串的暴力匹配,无非是做一次1-n的循环,

对于每个i,都枚举一个1-m,对于A[i-j+1...i]和B[1....j]都必须匹配

当j=m时,则满足了模式串与主串匹配

现在我们考虑,当一次匹配失败后,是否有可以继承的东西,以便于在下一次查找中更加快速呢?

我们发现当A[i-j+1....i+1]与B[1....j+1]不匹配时,可以考虑减小j值,

使得A[i-j+1...i]与B[1....j]保持匹配并继续匹配A[i+1]与B[j+1]

仔细思考后发现,我们需要在B[1...j]中找出最大的k,使得B[1...k]与B[m...m-k+1](后缀)完全相等

于是我们就将模式串做“自我匹配”,使得算法能够更加快速的进行,自我匹配的过程等会说

当我们不停地寻找更小的k,然而一直不能满足条件直到k=0时,我们就不停地i++,直到A[i]=B[1],再重复前面的流程

三、nxt数组(即跳转数组)的求值 

这里定义一个数组P,表示当模式串匹配到下标J时,而j+1与A串不匹配,这时最大的k

接下来开始计算数组P

我们对于一个未知的P[j],与许多已知的P[1...j-1],是可以通过P[1....j-1]来得到P[j]的,具体如下:

我们对于一个新的P[j],如果要直接从P[j-1]继承的话,就需要满足B[P[j-1]+1]=B[j]

如果不满足以上条件的话,那么我们考虑将指针k(初始为j-1)移到P[j-1]上,接着开始匹配B[k+1]=B[j],直到满足

如果一直不能匹配的话,那么P[j]=0;如果能够匹配的话,P[j]=P[k]+1;

四、例题(luoguP3375)

地址: https://www.luogu.org/problemnew/show/P3375

#include<bits/stdc++.h>
using namespace std;
char A[1000010];
char B[1000010];
int n,m,p[1000010];
int j=0;
int main(){
    scanf("%s%s",A+1,B+1);
    n=strlen(A+1);m=strlen(B+1);
    for(int i=2;i<=m;i++){
        while(j&&B[j+1]!=B[i]) j=p[j];
        if(B[j+1]==B[i]) j++;
        p[i]=j;
    }
    j=0;
    for(int i=1;i<=n;i++){
        while(j&&B[j+1]!=A[i]) j=p[j];
        if(B[j+1]==A[i]) j++;
        if(j==m){printf("%d\n",i-m+1);j=p[j];} 
    }
    for(int i=1;i<=m;i++)printf("%d ",p[i]);
    return 0;
} 

猜你喜欢

转载自www.cnblogs.com/heqingyu/p/10446587.html
kmp
今日推荐