KMP算法解释:理解关于next[j]数组的求解问题

一、算法背景介绍(我们为什么要采用这种算法?)
1.补充定义:
(1)主串:待匹配的大字符串
(2)模式串:我们希望在主串中匹配到的字符串
2.从暴力匹配到KMP算法
(1)暴力匹配算法
谈到KMP算法,离不开比较的是我们的暴力匹配算法,即从主串和模式串第一个字符开始比较,当出现失配时,我们将模式串中的比较字符回到第一位,主串回溯到开始比较的字符的下一位。代码如下。

点击查看代码
int IndexString(char* Original_string, char* Pattern_string) {
	int length_OriStr = strlen(Original_string);
	int length_PatStr = strlen(Pattern_string);
	for (int i = 0; i < length_OriStr; ) {//for循环相比while更加直观,两个终止条件均为不到字符串终点
		for (int j = 0; j < length_PatStr; ) {
			if (*(Original_string + i) == *(Pattern_string + j)) {
				i++;
				j++;
			}
			else {//失配
				j = 0;
				i = i - j + 1;
			}
			if (j == length_PatStr) {
				return i - j;//返回匹配的字符串在主串中第一个相同字符的下标。
			}
		}
	}
	return False;//未找到
}
(2)对暴力匹配算法进行分析,容易知道它的时间复杂度为O(m*n),m为主串长,n为模式串长,其时间复杂度较高,为此有时间复杂度更低的kmp算法。

二、算法的具体实现(附代码)
1.关于串的定义

点击查看代码
#define True 1
#define False 0

typedef int status;

typedef struct {
	char* string;
	int length;
}HString;
2.求解next[j]数组
点击查看代码
status Getnext(int* next,HString Pattern_string) {//得到nextj数组
	if (next == NULL) {
		return False;
	}
	next[0] = -1;
	int k = next[0];//k用来存放对应next[j]
	int j = 0;
	while (j < Pattern_string.length-1) {
		//因为最后会有j++,所以要有length-1,否则超限;或者理解为每次都在求解当前位置对应下一位置的next[j]值,所以要-1
		if (k == -1 || *(Pattern_string.string + k) == *(Pattern_string.string + j)) {//没有匹配字符或当前两个字符相等
			j++;
			k++;
			next[j] = k;//后一个的nextj=前一个nextj+1
		}
		else {
			k = next[k];//回溯
		}
	}
	return True;
}
3.使用生成的next数组进行模式串匹配
点击查看代码
int Index(HString Original_string, HString Pattern_string) {
	int judge = -1;//标识符,默认模式串不存在
	int i = 0, j = 0;//i用来遍历主串,j用来跳转模式串
	int numx = 0;//用来保存每个第一次比较的字符在主串中的位置

	int* next = (int*)malloc(sizeof(int) * Pattern_string.length);//nextj数组
	if (next == NULL) {
		exit(1);
	}
	Getnext(next, Pattern_string);//调用上面的函数求得模式串next[j]数组

	for (i = 0; i < Original_string.length; ) {
		if (j==-1 || *(Original_string.string + i) == *(Pattern_string.string + j)) {
			i++;
			j++;
			if (j == Pattern_string.length) {//完全相同后跳出
				break;
			}
		}
		else {
			j = *(next+j);//跳转到next[j]对应位置
		}
	}
	if (j == Pattern_string.length) {
		judge = i - j + 1;//返回完全匹配后,模式串中第一个字母在主串第一次出现的次序(不是下标)
	}
	free(next);
	return judge;
}

三、算法的理论推导
写了四页纸,比较草,抽空整理一下传上来。
四、算法总结
1.算法步骤:
(1)先进行模式串“自己和自己的比较”,以此求得next[j]数组,从而在后续的主串与模式串匹配中直接跳跃,避免重复性的“自己和自己比较”。
(2)然后利用next[j]数组进行比较
2.评价:经典算法,算是学到的第一个理解透彻比较困难的算法,自己进行推导有助于理解,理解后算法不需要死记硬背即可写出。

posted @ 2022-12-24 15:56  星外雨前  阅读(110)  评论(0编辑  收藏  举报