KMP:n+1次探

如你所见,这是我不知道第几次学KMP了。
推荐B站上电子科大的字符串专题。

引入:KMP是干什么的

KMP解决的是模式串P在源串T中出现次数的问题,比如模式串P为aba,源串为abababa,我们可以求出计算重叠的出现次数3,还可以求出不计算重叠的出现次数2。

next数组

前(后)缀和真前(后)缀:字符串s前i个(i<=strlen(s))字符为其前缀,i!=strlen(s)时为真前缀,后缀和真后缀同理。

next[i]表示模式串P以i为尾的这个前缀,最长的公共真前缀和真后缀长度,例如abcabc,next[1:6]={0,0,0,1,2,3}

求next数组

假设我们已经知道next[1:i-1],求next[i]。

设last=next[i-1],则p[1:last]等于p[i-last:i-1],即模式串长度为i-1的前缀,前last个与后last个相同,且last最大,那么我们只需要检测p[i]与p[last+1]是否相等,相等就是last+1,否则要在这last个(p[i-last:i-1],即p[1:last])里面找,更新last=next[last],继续检测p[i]与p[last+1]是否相等,如此循环直到last=0,p[i]=p[1],即为1,否则为0。

那么只需要设置next[1]=0,循环求即可。

真正的KMP

通过求next数组,我们发现:next数组的作用是当前面的匹配好了,而下一个匹配不到时,更新一个更小的来匹配,取代了重新匹配,来加速匹配。

KMP的过程与求next的过程几乎完全相同。

我们设last为匹配到T[i-1]时当前已经匹配的个数,即当last=strlen( P )时,匹配成功,现在求匹配T[i]时的last。

我们已经匹配了last个,p[1:last]等于t[i-last:i-1](是不是很熟悉),如果p[i]与t[last+1]相同,last++,否则跳转到前面,last=next[last],尝试更小的匹配,直到完全不能匹配。

代码

作用:输出有多少次匹配,可重叠。

由于通常的输入从0开始,这个代码的next数组从0到n-1,每一位比正常的next数组少1。

回到开头的问题,如果想求的匹配不重叠,后面每当匹配到,last=0即可。

#include<bits/stdc++.h>
using namespace std;
int n,last;
char t[1000020],p[1002000];
int nex[1002000];
int main(){
	scanf("%d",&n);
	while(n--){
		int ans=0;
		scanf("%s%s",p,t);
		int pl=strlen(p),tl=strlen(t);
		nex[0]=last=-1;
		for(int i=1;i<pl;i++){
			while(last>-1&&p[i]!=p[last+1]){
				last=nex[last];
			}
			if(p[i]==p[last+1]){
				last++;
			}
			nex[i]=last;
		}
		last=-1;
		for(int i=0;i<tl;i++){
			while(last>-1&&t[i]!=p[last+1]){
				last=nex[last];
			}
			if(t[i]==p[last+1]) last++; 
			if(last+1==pl){
				ans++;
				last=nex[last];
				//printf("%d\n",i-pl+2);
			}
		}
		/*for(int i=0;i<pl;i++){
			//if(i!=0) printf(" "); 
			//printf("%d",nex[i]);
		}*/
		printf("%d\n",ans);
	}
	return 0;
}


posted @ 2019-08-10 13:46  CCWUCMCTS  阅读(196)  评论(0编辑  收藏  举报