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\) 号字符匹配,我们就在一定程度上保留了原先的匹配信息。
我们首先需要对匹配串跑 \(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;
}