KMP&扩展KMP

声明

本文将不断加入例题,稍安勿躁,今天的总结争取9:30写完.

KMP

KMP,中文名字叫字符串匹配,用于解决一类字符串匹配问题.

先下一些定义:

  • \(s\)表示匹配串,\(t\)表示文本串,字符串匹配用于求\(s\)\(t\)中的出现情况.
  • \(n\)\(m\)分别为\(s\)\(t\)的字符串串长.
  • \(nxt_i\)表示对于\(s\)的前缀\(s_{1...i}\)的最长公共前后缀.

首先我们先想一想\(nxt_i\)对于求解问题有怎样的帮助.

暴力匹配

我们对于每一个\(t_i=s_1\)的位置都匹配一次,这样子复杂度为\(\Theta(n*m)\)的.

优化

考虑在暴力匹配中其实我们不一定要重新匹配,因为你画一个图感性理解,发现如果匹配到了\(s\)\(i\)前缀,到\(i+1\)失配,其实是可以从\(nxt_i\)重新开始匹配的.

这个时候\(nxt_i\)的好处就体现出来了,我们可以迅速再一次匹配.

代码实现

void KMP(){
	nxt[1]=0;int j=0;
	for(int i=2;i<=n;i++){
		while(j && s[i]!=s[j+1])j=nxt[j];
		if(s[i]==s[j+1])j++;nxt[i]=j;
	}
	j=0;
	for(int i=1;i<=m;i++){
		while(j && s[j+1]!=t[i])j=nxt[j];
		if(t[i]==s[j+1])j++;
		if(j==n){ans.push_back(i-n+1);j=nxt[j];}
	}
}

扩展KMP

\(KMP\)没有半毛钱关系,甚至和\(KMP\)的思想都没有半毛钱关系.

问题引入

有两个字符串\(a\),\(b\),要求输出\(b\)\(a\)的每一个后缀的最长公共前缀。

还是和往常通过一样的,从暴力入手。

暴力求解

枚举每一个\(a\)的后缀然后与\(b\)匹配,复杂度\(\Theta(n*m)\)的.

其他做法

你当然可以\(Hash\),但是这与我们讨论的东西无关。

优化

我们大致画一下图(很重要)发现显然之前匹配过的位置如果包含这个\(i\)后缀的第一个字符,那么可以从那一个最长匹配开始.

剩下的只需要不断像\(manacher\)一样匹配即可.

代码实现

for(int i=1,l=0,mx=0;t[i];i++){
	z[i]=(i<mx)?min(z[i-l],mx-i):0;
	while(t[i+z[i]]==t[z[i]])z[i]++;
	if(i+z[i]>mx){l=i;mx=i+z[i];}
}

总结

大致就是这样子了,今天还是有点东西的.

posted @ 2019-10-04 21:44  fexuile  阅读(303)  评论(0编辑  收藏  举报