KMP

KMP

\(KMP\),一个可以在 \(O(n+m)\) 的时间复杂度内求解字符串匹配的算法。
\(n\) 为模式串长度,\(m\) 为文本串长度)

首先,我们有 \(O(nm)\) 算法,我们可以枚举文本串的每一个位置作为出发点,然后一位一位匹配,在随机数据下表现良好,但很好被卡。

其次,基于我们几近万能的 \(hash\) 算法,我们优化了 \(O(n^2)\) 的暴力,通过二分 \(hash\) 将其复杂度优化为 \(O(mlogn)\)

我们当然不想止步于此,所以有三位伟大的计算机学家发明了伟大的 \(KMP\)

\(KMP\) 的基本思想是用之前的信息去更新当前的信息,在此之中可能存在失配的情况,我们要记录一个失配指针,使得我们快速的到达能匹配的位置。

我们记 \(i\) 位置的失配指针为 \(next_i\),同时,\(next_i\) 也是 \(i\) 的最长的相等真前后缀的长度,所以我们可以快速匹配。

举个例子,如下图,我们的第五个位置与文本串冲突了,但我们不想从头开始匹配,那么我们就可以用匹配串的 \(1-next_4\) 个字符 与文本串的 \(3-4\) 号字符匹配,我们就在一定程度上保留了原先的匹配信息。

img

我们首先需要对匹配串跑 \(KMP\),求出匹配串的每一个前缀的 \(border\),然后再在文本串上跑一样的内容即可。

\(code\)

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e6+10;
char a[N],b[N];
int nest[N];
int main(){
    scanf("%s%s",a+1,b+1);
    int len1=strlen(b+1);
    int j=0;
    nest[1]=0;
    for(int i=2;i<=len1;i++){
        while(j&&b[i]!=b[j+1])j=nest[j];
        if(b[i]==b[j+1])j++;
        nest[i]=j;
    }
    int len2=strlen(a+1);
    j=0;
    for(int i=1;i<=len2;i++){
        while(j&&a[i]!=b[j+1])j=nest[j];
        if(a[i]==b[j+1])j++;
        if(j==len1)printf("%d\n",i-j+1);
    }
    for(int i=1;i<=len1;i++)printf("%d ",nest[i]);putchar('\n');
    return 0;
}
posted @ 2022-10-27 21:57  hxqasd  阅读(56)  评论(0)    收藏  举报