KMP算法

算法概述

KMP算法的基本思想是:通过分析模式串P,得出一个next[]数组,以指示:当源串字符S[i]与P[j]不匹配时,下一步应比较S[i]和P[next[j]],若仍不匹配,则比较S[i]和P[next[next[j]]]...如此循环。因此在整个过程中,指向S的指针i是始终不回溯的,指向P的指针j会根据当前匹配状况按next[]数组的值回溯。假设S和P的长度分别为n和m,则KMP算法的时间复杂度为O(n+m)


关于next[]

1.next[j]的意义是:当S[i]与P[j]不匹配时,下一步应比较S[i]与P[next[j]]

2.next[]只与P有关,与S无关

3.next[j]的值等于子串{P[1], ..., P[j-1]}的最大匹配前后缀长度

如P为 a b a a b c a c ,下标从0开始,则对于P[5] = 'c'来说,next[5] = 2。因为a b a a b的最大匹配前后缀是a b,其长度为2

4.next[]的计算:假设next[j] == k,即有{P[1], ..., P[k-1]} ~ {P[j-k+1], ..., P[j-1]},若

  • P[k] == P[j],则必然有next[j+1] = k+1
  • P[k] != P[j],则比较P[next[k]]和P[j],若仍不匹配,则比较P[next[next[k]]]和P[j],直至匹配(这实际上是一个已知next[]而进行的KMP匹配过程)
5.next[0] = -1, next[1] = 0 是固定的。next[0] = -1是作为一个标志界限,表示若S[i]和P[0]仍不匹配,此时只能将指针i向右移动一位再与P[0]比较
next[1] = 0表示若S[i]与P[1]不匹配,下次只能比较S[i]与P[0]
6.i++, j++有两种情况:
  • P[j] == S[i],当前匹配,则应继续匹配下去
  • j == -1时(由next[0]得到),此时应i++, j++,从而使得下次比较P[0]与i右移一位所指字符

改进
若出现P[j] == P[next[j]]的情况,那么在KMP过程中,若S[i] != P[j],则必然有S[i] != P[next[j]]
对此我们的改进是,在生成next[]时,出现上述情况后,循环next[j] = next[next[j]],直至P[j] != P[next[j]]或j == -1

代码
void get_next(char * p, int next[]){
	assert(p != NULL);
	assert(next != NULL);

	next[0] = -1;
	next[1] = 0;  //next[0]和next[1]的值是固定的
	int i = 1, j = 0;
	int len_p = strlen(p);
	while(i < len_p){
		if(j == -1 || p[i] == p[j]){  //i++,j++的两种情况。注意判断条件的次序,利用短路特性避免数组下标越界
			i++, j++;
			next[i] = j;
			while(next[i] != -1 && p[i] == p[next[i]])  //改进部分
				next[i] = next[next[i]];
		}
		else j = next[j];  //若不匹配,一直next下去直至匹配或j == -1
	}
}

int kmp(char * s, char * p){
	assert(s != NULL);
	assert(p != NULL);

	int i = 0, j = 0;
	int len_s = strlen(s), len_p = strlen(p);
	int * next = (int *)malloc(len_p*sizeof(int));
	get_next(p, next);

	while(i < len_s && j < len_p){  //和get_next()函数类似
		if(j == -1 || s[i] == p[j]) i++, j++;
		else j = next[j];
	}
	if(j == len_p) return 1;  //匹配成功
	else return 0;
}

尚存问题
未考虑存在多个匹配的情况

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值