开始从重新学字符串了,改过自新吧…

​洛谷P3375 【模板】KMP字符串匹配​


定义模式串为 b b b,文本串为 a a a(很长)

假如我们处理出了一个 b b b的 k m p kmp kmp数组

其中 k m p [ i ] kmp[i] kmp[i]表示 [ 1 , i ] [1,i] [1,i]中的最长公共前后缀,有什么办法加速匹配吗??

比如匹配

a=abcacababcab
b=abcab

比如现在匹配到第五位,发现 b [ 5 ] ! = a [ 5 ] b[5]!=a[5] b[5]!=a[5],怎么办??把 b b b右移一位继续匹配??

实际上,我们可以直接右移三位

abcacababcab
abcab

为什么?? 感性理解就是前后有字母相同,不需要再重复比较

因为 k m p [ 4 ] = 1 kmp[4]=1 kmp[4]=1,所以可以直接匹配从模式串的第二个字符开始尝试匹配。。。

int j = 0;//初始模式串匹配位置为0
for(int i=1;i<=n;i++)
{
while( j&&b[j+1]!=a[i] ) j = kmp[j];//适配,但[1,j]仍然是匹配的,所以跳kmp
if( b[j+1]==a[i] ) j++; //经过上面跳跃回溯,找到一个匹配的了
if( j==m ) { printf("%d\n",i-m+1); j = kmp[j]; }//匹配成功
}

如何处理 k m p kmp kmp数组??原理一样,让模式串自己匹配自己即可

int j = 0;//当前匹配到低第几位 
for(int i=2;i<=m;i++)
{
while( j&&b[i]!=b[j+1] ) j = kmp[j];
if( b[j+1]==b[i] ) j++;//这里只要j不为零就说明可以匹配
kmp[i] = j;
}

模板

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e6+10;
char a[maxn],b[maxn];
int n,m,kmp[maxn];
void KMP()
{
int j = 0;//当前匹配到低第几位
for(int i=2;i<=m;i++)
{
while( j&&b[i]!=b[j+1] ) j = kmp[j];
if( b[j+1]==b[i] ) j++;//这里只要j不为零就说明可以匹配
kmp[i] = j;
}
j = 0;
for(int i=1;i<=n;i++)
{
while( j&&b[j+1]!=a[i] ) j = kmp[j];
if( b[j+1]==a[i] ) j++;
if( j==m ) { printf("%d\n",i-m+1); j = kmp[j]; }
}
for(int i=1;i<=m;i++) printf("%d ",kmp[i] );
}
int main()
{
scanf("%s%s",a+1,b+1);
n = strlen( a+1 ); m = strlen( b+1 );
KMP();
}