【学习笔记】KMP字符串匹配

字符串匹配——KMP算法

给定两个字符串s1和s2,询问s2在s1中出现的位置(定义为出现时的第一个字符在s1中的位置)

暴力枚举

看到字符串匹配(如果你还不会KMP/Hash的话),八成是要用暴力枚举的方法(可是这样极其容易被卡掉)因此三位神犇发明了KMP算法

顺便%一下dalao

D. E. Knuth 、J.H. Morris and V.R.Pratt

next数组

KMP算法的核心操作

为了方便,我们将s1定义为主串,s2定义为模式串

可以从左到右一位一位的匹配,失配了就从头再来(这是暴力枚举)

所以我们可以想更优的方法,利用已经匹配的部分,从而只移动模式串,让主串保持不变,可以用指针j表示模式串匹配到了哪一位

举个栗子

ABCABCDEFG
ABCABB

发现匹配到第六位时失配力,此时j在B也就是第六位

可以移动一下

ABCABCDEFG
   ABCABB

此时j在C的位置(第三位)力

可以看出,每次移动后,假如移动j到了k,则字符串前k-1位==j之前的k-1位

所以我们可以用next数组来记录对于每一个j的k的位置,即最长公共前后缀de长度

而对于第1位/第2位,其最长公共前后缀的长度始终为1

即next[0]=1,next[1]=1;

预处理&匹配

上文介绍了next数组以及指针j如何移动

那么怎么预处理next数组呢?

其实很简单

让模式串与自己匹配就好啦

	for(int i=2;i<=len_b;i++)
	{
		while(j && b[j+1]!=b[i])
		{
			j=kmp[j];
		}
		
		if(b[j+1]==b[i])
		{
			j++;
		}
		
		kmp[i]=j;
	}

然后就是主串与模式串之间的匹配

	for(int i=1;i<=len_a;i++)
	{
		while(j && b[j+1]!=a[i])
		{
			j=kmp[j];
		}
		
		if(b[j+1]==a[i])
		{
			j++;
		}
		
		if(j==len_b)
		{
			cout<<i-j+1<<endl;
			j=kmp[j];
		}
	}

完整の板子

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>

using namespace std;

const int maxn=1e6+5;

int kmp[maxn],j;

int len_a,len_b;

char a[maxn],b[maxn];

int main()
{
	cin>>a+1;
	cin>>b+1;
	
	len_a=strlen(a+1);
	len_b=strlen(b+1);
	
	for(int i=2;i<=len_b;i++)
	{
		while(j && b[j+1]!=b[i])
		{
			j=kmp[j];
		}
		
		if(b[j+1]==b[i])
		{
			j++;
		}
		
		kmp[i]=j;
	}
	
	j=0;
	
	for(int i=1;i<=len_a;i++)
	{
		while(j && b[j+1]!=a[i])
		{
			j=kmp[j];
		}
		
		if(b[j+1]==a[i])
		{
			j++;
		}
		
		if(j==len_b)
		{
			cout<<i-j+1<<endl;
			j=kmp[j];
		}
	}
	
	for(int i=1;i<=len_b;i++)
	{
		cout<<kmp[i]<<" ";
	}
	
	return 0;
}

無限はゼロに近いが、ゼロではない可能性がある

posted @ 2022-09-22 09:21  NinT_W  阅读(11)  评论(0编辑  收藏  举报