(八)KMP算法解析

KMP

KMP也就是朴素匹配算法的优化,加快了匹配效率。
他的关键点就在于:问题是由模式串(字串)决定,而不是目标串决定。
他的核心就在于:减少不必要的回溯
回溯:当匹配时失配后应该用子串中的哪个元素继续和目标串匹配。

匹配过程讲解

例如:
在这里插入图片描述
S为目标串,T为模式串(子串),对于朴素的BF算法来说,当匹配到第五个元素的时候,出现了失配的现象,那我们该如何解决,也就是减少不必要的回溯,通过我们的计算来减少:
在这里插入图片描述
我们可以看到,当匹配到第五个元素的时候,如果是BF算法的话,大家都知道是要i2和j1匹配,但是我们可以通过计算发现这是不必要的回溯:
j1=i1,j2=i2;
我们通过模式串可以明显地看出:j1!=j2;所以推出j1!=i2;同理可得j1!=i3;j1!=i4;
所以我们可以直接跳过中间的回溯,直接让j1和i5匹配:在这里插入图片描述
像这样,我们通过减少不必要的回溯就可以大大优化这个匹配算法,这就是KMP了。
当然,大家都知道,KMP算法最重要的是写出next数组,也就是如果出现失配,字串应该回溯到哪个元素的一个数组。
通过上面的例子,我们可以很快地写出T的next数组:
在这里插入图片描述

next数组求解方法

再举一个例子:
在这里插入图片描述
next数组从1开始第一个元素一定是0,第二个元素一定是1,第三个元素我们发现前面两个元素并不相等,所以如果在第三个元素失配的情况下,直接回溯到第一个元素:
在这里插入图片描述
i1=i3,所以next[4]=2;
i1i2=i3i4;所以next[5]=3;
i1i2i3=i3i4i5;所以next[6]=4;
i1=i6;所以next[7]=2;
i1=i7;所以next[8]=2;
i1i2=i7i8;所以next[9]=3;
在这里插入图片描述
我们需要判断这个元素之前有多少个前缀与后缀相同的元素,再加一就得到了next数组。

代码时刻

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

typedef char* string;

int get_next(string T,int* next) {
	int i = 1,
		j = 0;
	next[1] = 0;
	while (i < T[0]) {
		if (0 == j || T[i] == T[j]) {
			i++;
			j++;
			if (T[i] != T[j]) {
				next[i] = j;
			}
			else {
				next[i] = next[j];
			}
		}
		else {
			j = next[j];
		}
	}
	return 0;
}
int KMP(string S,string T,int* next) {
	int i = 0,
		j = 0;
	while (i < S[0] && j < T[0]) {
		if (0 == j||S[i] == T[j])
		{
			i++;
			j++;
		}
		else
		{
			j = next[j];
		}
	}
	if (j >= T[0]) {
		return i - T[0] + 1;
	}
	else
		 return -1;
}
int main(void) {
	char S[255],T[255];
	int next[255];
	int value;

	printf("请输入主串字符串:");
	scanf_s("%s", S + 1,255);
	printf("\n请输入子串:");
	scanf_s("%s", T + 1,255);
	T[0] = 0;
	for (int i = 1; T[i]; i++) {
		T[0]++;
	}
	S[0] = 0;
	for (int i = 1; S[i]; i++) {
		S[0]++;
	}
	get_next(T,next);
	value = KMP(S,T,next);
	if (value != -1)
		printf("\n%d\n", value);
	else
		printf("主串中没有子串");
	return 0;
}

今天就不讲解代码啦!大家可以用自己的思路先尝试一下。
当然没有调试结果的代码就不是好代码:
在这里插入图片描述

总结

下次就学树啦!我们下次再见!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小光学嵌入式

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值