0
点赞
收藏
分享

微信扫一扫

【模板】KMP字符串匹配

zhoulujun 2022-02-13 阅读 87

KMP字符串匹配中next数组求法

char* strstr(char*str,char*pattern);//字符串很小O(m.n) 

子串里面一头一尾有可以匹配上的小串,指针不用每次都回溯到最最开头的位置。
匹配前,对模式串进行详细分析, 搞清楚next值的含义
p[0 ~ j]==p[ (i-j) ~i] ,即从p[0]开始长度为len=j+1,都有p[j]=
p[i] 那么一旦p[i+1]与t[?]不相等了,t[?]下一个就要与p[j+1]比较,next[i+1]=j+1
例如next[6]=3 ,模式串从0-5这个子串中,首尾能匹配的
小串从0开始这个小串尾部的下标 再往后挪一位

其实我们也可以发现, KMPKMP 算法之所以快,不仅仅由于它的失配处理方案,更重要的是利用前缀后缀的特性,从不会反反复复地找,我们可以看到代码里对于匹配只有一重循环,也就是说 KMPKMP 算法具有一种“最优历史处理”的性质,而这种性质也是基于 KMP 的核心思想的。

在这里插入图片描述
t[?]与p[0]都匹配失败了,
-1代表主串里面这个点不可能成功了,主串往后挪一个位置
把模式串最前面对着失败的位置 继续匹配

如果是与 p [ x ] ( x ! = 0 ) p [x](x!=0) p[x](x!=0)比较失败了,那么t[?]与next[x]匹配

这种形式可以成功获得匹配的位置,但是有一个缺陷,next数组的不能正确反映 模式串中首尾匹配的小串的真正长度
没有缺陷,根据观察比对,发现next【i】 i ∈ ( 0 — — p . s i z e ( ) − 1 ) i∈( 0——p.size()-1 ) i(0p.size()1)表示了模式串前i个字符,即0~ i-1的这一段首尾匹配小串的长度,可是0~p.size()-1整个模式串这一段的首尾匹配小串长度却不可知。观察getNext()函数里对next数组的求值就可以发现,next数组的长度是 p . s i z e ( ) + 1 p.size()+1 p.size()+1而不是 p . s i z e ( ) p.size() p.size(),也就是存在 n e x t [ p . s i z e ( ) ] next[p.size()] next[p.size()], 可以表示0~p.size()-1整个模式串首尾匹配小串长度。
在这里插入图片描述
在这里插入图片描述

【模板】KMP字符串匹配

【模板】KMP字符串匹配

#include <iostream>
#include <algorithm>
using namespace std;
const int N=1e6+5;
int next[N];
void getNext(string p){
	next[0]=-1;
	int i=0;
	int j=-1;
	//在模式匹配串中首尾匹配的小串中,i指向尾小串最后一个字符
//	 j指向首小串最后一个字符,
//	 也就是p[i+1]匹配不上时指针回溯的位置 j+1就要靠j定位 
	int len=p.size();//strlen 
	while(i<len){
		if(j==-1||p[i]==p[j]){
			i++;j++;//首尾小串依旧匹配就继续向前
			next[i]=j;//相当于是p[i]=p[j]则next[i+1]=j+1 
//至于j==-1,就是指针回溯到从-1开始比较,p[i+1]肯定就是从j==0开始比较
 
		}
		else{
			j=next[j];//如果p[i]!=p[j],那么 p[i]就和next[j]比较 
		} 
	} 
}
void kmp(string t,string p){
	int lt=t.size();
	int lp=p.size();
	int i=0;
	int j=0;
	while(i<lt&&j<lp){
		if(j==-1||t[i]==p[j]){
			i++;
			j++;
		}
		else j=next[j];//这样j就可能为-1,继而想到if中要判断j==-1 
		//j==-1模式串从0开始即j++,t[i]正是不能与p[0]匹配 
		if(j==(lp)){
		cout<<i-lp+1<<endl;
		j=next[j];//回溯到p【0】
	}	
}	
} 
int main(){
	string t;
	string p;
	cin>>t>>p;
	getNext(p); 
 	kmp(t,p);
 	for(int i=1;i<=p.size();i++){
	 //next数组长度有p.size()+1,
//	 后p.size()个值才是模式串里首尾匹配小串的长度 
 		cout<<next[i]<<" ";
	 }

    return 0;
}

还有种写法,避免next值取-1的

#include<iostream>
#include<cstring>
#define MAXN 1000010
using namespace std;
int next[MAXN];
int la,lb,j; 
char a[MAXN],b[MAXN];
int main()
{
    cin>>a+1;
    cin>>b+1;
    la=strlen(a+1);
    lb=strlen(b+1);
    for (int i=2;i<=lb;i++)
	   {     
	   while(j&&b[i]!=b[j+1])
        j=next[j];    
       if(b[j+1]==b[i])j++;    
        next[i]=j;
       }
    j=0;
    for(int i=1;i<=la;i++)
	   {
          while(j>0&&b[j+1]!=a[i])
           j=next[j];
          if (b[j+1]==a[i]) 
           j++;
          if (j==lb) {cout<<i-lb+1<<endl;j=next[j];}
       }

    for (int i=1;i<=lb;i++)
    cout<<next[i]<<" ";
    return 0;
}
举报

相关推荐

0 条评论