KMP算法及其优化

      今天来记录一下,关于字符匹配的KMP算法。

1.引言

      给定字符串text和pattern,需要判断字符串pattern是否为test的子串。pattern一般称为模式串,text为文本串。若匹配成功,则让函数返回,匹配开始处的下标,否则,返回-1。

2.KMP中的核心—next数组的求解

      假设有一个字符串s,它以i号位作为结尾的子串就是s[1…i]。对该字串来说,长度为 k+1 的前缀和后缀分别是 s[0…k] 和 s[i - k…i] 。现在定义一个 next 数组,其中 next[i] 表示使子串 s[0…i] 的前缀 s[0…k] 等于后缀 s[i-k…i] 的最大的 k (注意:前缀后缀可以部分重合,但不能是 s[0…i] 本身),如果找不到相等的前后缀,就令next[i] = -1。显然 next[i] 就是所求最长相等前后缀中前缀的最后一位的下标。

//对于next数组而言,next[0]总是等于-1,因为对第一位来说,其不存在公共前后缀,而又为了其他位比较时,从上一位next[0]多一位的1开始比较。
void GetNext(char pattern[], int m){
	next[0] = -1;
	int j = -1;
	for(int i = 1; i < m; ++i){	//对该字符串每一位进行next数组的求解
		while(j != -1 && pattern[i] != pattern[j + 1]){	//j == -1是边界条件
			j = next[j];
		}
		if(pattern[i] == pattern[j + 1]){
			j++;	//令next[i] = j + 1,令next指向该位置
		}
		next[i] = j;	//每次让j记录上一字符的next值
	}
	return;
}

3.KMP算法

//对于该算法,只有pattern上的指向在不断由于i的更新改变
int KMP(char text[], char pattern[], int n, int m){	//n和m分别为text和pattern数组的长度
	GetNext(pattern, m);	//计算pattern的next数组
	int j = -1;	//初始化j为-1,代表当前没有任意一位被匹配
	for(int i = 0; i < n; ++i){
		while(j != -1 && text[i] != pattern[j + 1]){
			j = next[j];
		}
		if(text[i] == pattern[j + 1]){
			j++;	//text[i]与pattern[j + 1]匹配成功,令j加一
		}
		if(j == m - 1){	//pattern完全匹配
			return i - j;
		}
	}
	
	return -1;	//不匹配返回-1
}

      上述代码与求解next的代码惊人的相似,其实求解next数组的过程就是模式串pattern进行自我匹配的过程。
      计算mext数组的时间复杂度为O(m),匹配过程的时间复杂度为O(n),故KMP算法的时间复杂度为O(m + n)。

4.优化next数组

      上述代码中关于匹配中回退,从匹配的角度看,每次匹配不成功,next[j]表示当模式串的 j + 1 位失配的时候,j应当退到的位置,仔细思考,每次我们退的时候,j会进行多次无意义的回退,即他总是回到 next[j] 的位置,但全然不管 pattern[j + 1] == pattern[next[j] + 1] 的情况,即这一次回退之后还是匹配不上。要让 next[j] 进行改变,一步回退到恰当的位置,就要对 next 数组进行变化。优化后 nextval 数组的含义为当模式串 pattern 的 i+1 位发生失配的时候,i 应当回退到的最佳位置。

void GetNextval(char pattern[], int m){
	nextval[0] = -1;
	for(int i = 1; i < m; ++i){	//对该字符串每一位进行next数组的求解
		while(j != -1 && s[i] != s[j + 1]){	//j == -1是边界条件
			j = nextval[j];
		}
		if(s[i] == s[j + 1]){
			j++;	//令next[i] = j + 1,令next指向该位置
		}
		if(j == -1 || s[i + 1] != s[j + 1]){
			nextval[i] = j;	//每次让j记录上一字符的next值
		}
		else{
			nextval[i] = nextval[j];
		}
	}
	return;
}

      注:nextval只是跳过了无意义的匹配,并不会导致漏解,而由nextval的含义,GetNextval 和 KMP中的 while 都可以换成 if ,因为最多只会执行一次。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值