kmp算法next计算方法_KMP(看*片算法)算法详解

KMP算法又称看 *片算法。 哈哈哈,不要当真了。

看看百度的解释:KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)。

KMP是三个人的名字首字母。

主要解决的是字符串匹配的问题。

有一个主串 str1 子串str2,要查询str1中是否有str2,如果有返回出现的第一个位置,如果没有返回-1.

正常的做法,我们遍历str1的每个位置,判断接下来是否是str2,

时间复杂度是O(n*m)

要优化,首先,我们肯定要遍历str1,也要遍历str2.

那么不管怎么优化m和n都存在。

KMP算法,优化到了O(M+N)

定义一个概念,对于string中某个字符串,他有一个值,定义为这个字符前面最长前缀和最长后缀的相同匹配长度。

不包括这个字符前面的字符串的整体长度。

很懵逼吧,举个例子。

5ac621316427699de04ec6457a8af301.png

对于?这个字符串,前面的字符串,aaaaa。

前缀,后缀 为1时是a,相等。

前缀,后缀 为2时是aa,相等。

前缀,后缀 为3时是aaa,相等。

前缀,后缀 为4时是aaaa,相等。

前缀,后缀 为5时是aaaaa,这个时候前缀后缀代表这个字符串肯定相等,无效信息,所以我们取4。

再来一个例子,

af94349a4ce9da6948dea4adf34d6cf8.png

这个值是3

对str2每个位置都求一个这个值。

eg:

e8f537d9a59704a6745a1756fe49e09d.png

对0位置求,0位置前面没有东西,设为-1

对1位置求,1位置前面只有1个字符,又不能取1,所以值是0

对2位置求,1

3,0

4,1

5,2

6,3

我们把这个值存在一个next数组里。

[-1,0,1,0,1,2,3]

后面会介绍如何更快的求next数组

不妨设,我们在str1的i位置开始匹配str2.

75fbaecc9d66da8baab265ceeca4b0d4.png

会发现,最后位置,x和y对不上。这时候暴力方法,我们会放弃i位置。

从i+1位置从新开始。、

KMP算法会从最大前缀后一个位置开始和x进行比对。

fa038ef69d3ccf9de6c29d97827b8048.png

这样的原则基于两个前提。

  1. 最大前缀和最大后缀的字符串相同,也等于str1中对应位置的字符串。
  2. 在中间的部分不可能出现能构成str2的字符串。
#include <iostream>
#include <string>
using namespace std;


int *getNextArray(string str2){
	int *next=new int[str2.size()];
	next[0]=-1;
	next[1]=0;
	int cn=0;//有两个含义,1.代表比对位置。2.代表最大后缀。 
	int i=2;
	while(i < str2.size()){
		if(str2[i-1]==str2[cn]){
			next[i++]=++cn;
			
		}
		else if(cn>0){
			cn=next[cn];
			//跳到下一个比对的位置 
		}
		else {
			next[i++]=0;
			//i这个位置,一直都没有找到。cn都跳出循环了,只能是0 
		}
	}
	
	return next; 
	
	
}

int getIndexOf(string str1,string str2){
	if(str1.empty() || str2.empty() || str1.length()<str2.length()) return -1;
	int *next=getNextArray(str2);
	int i1=0,i2=0;
	while(i1<str1.length() && i2<str2.length()){
		
		if(str1[i1]==str2[i2]){
			i1++;
			i2++;
		}
		else if (i2==0){
			i1++;
		}
		else{
			i2=next[i2];
		}
		
	}
	
	return i2==str2.length()? i1-i2 : -1;
	
	
}

void test_case1(){
	string a="aaaabb";
	string b="aab";

	cout<<getIndexOf(a,b);
		
	
}
int main(){
	test_case1();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值