KMP 字符串匹配算法

✅<1>主页:C语言的前男友
📃<2>知识讲解:KMP算法
🔥<3>创作者:C语言的前男友
☂️<4>开发环境:Visual Studio 2022
🏡<5>系统环境:Windows 10
💬<6>前言:KMP 算法是一个非常牛逼的字符串匹配算法

目录

一.KMP算法介绍

二.为什么存在 next 数组

三.子串退回位置的求解

四.next数组的代码求解:

第一种情况:

第二种情况:

五.代码实现及讲解:

代码:

测试:

 六.最后

不经一番寒彻骨,怎得梅花扑鼻香。


 

一.KMP算法介绍

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

二.为什么存在 next 数组

普通的 BF 算法,存在大量的多余匹配,为了减少模式串与主串的匹配次数以达到快速匹配的目的。来优化 BF 算法里面匹配失败后,将子串从头开始与模式匹配的问题。在实际的匹配当中,当模式串与子串匹配失败的时候,有很多的情况子串是不需要从头开始跟模式串匹配的。只需要退回到一个特定的位置。next数组就是一个长度跟子串的长度一样,数组中存储子串中每个字符在匹配失败后,需要退回的位置的数组。

当有了 next 数组,当子串与模式串匹配失败后,子串无需从最开始的位置与模式串匹配,只需从 next 数组中存储的位置开始再次与模式串匹配。

三.子串退回位置的求解

 其中我们将在子串第一个字符匹配失败退回位置记作 -1,在第二个字符匹配失败退回记作 0 。

这样根据上述方法就能求解出,子串每个位置匹配失败后的退回位置,将每个字符匹配失败后的退回位置放在一个长度与子串长度相等数组中,这个数组就是 next 数组。

四.next数组的代码求解:

第一种情况:

在求 next 数组的时候,当前位置 i 匹配失败后,k 代表退回的位置,如果 k 所在的位置的字符,与 i 所在位置的字符相等时,则 i+1 位置匹配失败后,退回的位置就是,k+1;

第二种情况:

当前位置 i 匹配失败后,k 代表退回的位置,如果 k 所在的位置的字符,与 i 所在位置的字符不相等时,此时,就需要 k 继续按照当前位置的继续回退,直到回退到,k 位置的字符与 i 位置的字符相等,或者 k==-1.这个时候就回到了,第一种情况。

五.代码实现及讲解:

代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>

void GetNext(char* sub,int *next)
{
	int sublen = strlen(sub);
	int i = 2; // i 从数组子串第三个元素开始
	int k = 0; // 代表回退的位置,k的位置始终位于,i - 1 匹配失败的回退位置的对应的 k,
	           // 即可由 i - 1 位置求出 的 k,求出 i 位置的 k。
	next[0] = -1;
	next[1] = 0;
	while (i<sublen)
	{
		//当 k 回退到头的时候 即 k==-1,就需要回退到 0 号下标位置,
		//或者 如果 退回位置字符和匹配失败的字符相同
		if (k == -1 || sub[i - 1] == sub[k])
		{
			next[i] = k + 1;
			//已经求出 i 的退回位置,i 继续往后走
			i++;
			// k 要变成新的 i-1 位置的退回位置。
			k++;
		}
		//如果 退回位置字符和匹配失败的字符不相同
		else
		{
			// k 在当前位置继续回退
			k = next[k];
		}
	}
}

int KMP(char* str, char* sub, int pos)
{
	assert(str && sub);
	int StrLen = strlen(str);
	int SubLen = strlen(sub);
	assert(pos >= 0&&pos<StrLen);
	//创建 next 数组
    int * next = (int*)malloc(sizeof(int)*SubLen);
	
	int i = pos; //主串
	int j = 0;   //子串

	//构建next数组
	GetNext(sub,next);

	//主串和子串匹配
	while (i < StrLen && j < SubLen)
	{
		//如果匹配的字符相同就继续往后匹配
		if (j==-1 || str[i] == sub[j])
		{
			i++;
			j++;
		}
		//回退
		else
		{
			j = next[j];
		}
	}
	//当 j 遍历到最后时,也就是 子串全部匹配成功
	if (j >= SubLen)
	{
		return i - j;
	}
	//模式串与子串匹配失败
	return -1;
}

测试:

 六.最后

不经一番寒彻骨,怎得梅花扑鼻香。

 

 

 

 

  • 60
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 58
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

我的代码爱吃辣

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

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

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

打赏作者

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

抵扣说明:

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

余额充值