KMP算法原理

算法简介

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

算法原理

现在先给两个字符串,我们的目的就是要寻找模式串与主串中所有字符都匹配的位置,并返回模式串第一个元素的下标。
第一轮:模式串和主串的第一个等长子串比较,发现第0位字符一致,第1位字符一致,第2位字符不一致
在这里插入图片描述
我们先介绍一下最简单的匹配方法

1.BF算法

BF算法就是使用简单粗暴的方式,对主串和模式串进行逐个字符的比较
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
第二轮:模式串向后挪动一位,和主串的第二个等长子串比较,发现第0位字符不一致

在这里插入图片描述
第三轮:模式串继续向后挪动一位,和主串的第三个等长子串比较,发现第0位字符不一致
在这里插入图片描述
以此类推,直到第N轮
在这里插入图片描述
第N轮:此时我们可以发现模式串的所有字符都得到了匹配
在这里插入图片描述
以上就是BF算法的原理,我们可以发现,BF算法效率实在太低了,每一轮只能老老实实地把模式串右移一位,实际上做了很多无谓的比较。

2.KMP算法

KMP算法和BF算法有些是共通的,同样是把主串和模式串的首位对齐,从左到右对逐个字符进行比较。

现在先给两个字符串
在这里插入图片描述
第一轮:模式串和主串的第一个等长子串比较,发现前5个字符都是匹配的,第6个字符不匹配,是一个“坏字符”。
在这里插入图片描述
我们可以发现,在已经匹配的字符串中,前缀“GTGTG”的后三个字符“GTG”和前三位字符“GTG”是相同的。
在这里插入图片描述
在下一轮的比较时,只有把这两个相同的片段对齐,才有可能出现匹配。这两个字符串片段,分别叫做最长可匹配后缀子串和最长可匹配前缀子串

第二轮:我们直接把模式串向后移动两位,让两个“GTG”对齐,继续从刚才主串的坏字符A开始进行比较
在这里插入图片描述
显然,主串的字符A仍然是坏字符,这时候的匹配前缀缩短成了GTG:
在这里插入图片描述
按照第一轮的思路,我们来重新确定最长可匹配后缀子串和最长可匹配前缀子串:
在这里插入图片描述
第三轮:我们再次把模式串向后移动两位,让两个“G”对齐,继续从刚才主串的坏字符A开始进行比较
在这里插入图片描述
重复操作,直到匹配结束。
KMP算法的整体思路:在已匹配的前缀当中寻找到最长可匹配后缀子串和最长可匹配前缀子串,在下一轮直接把两者对齐,从而实现模式串的快速移动。

3.移动原理(next数组)

先介绍一下字符串前缀后缀的概念
在这里插入图片描述
移动大小
在这里插入图片描述
next数组计算方法:
在这里插入图片描述
在这里插入图片描述
next数组计算方法
在这里插入图片描述
在这里插入图片描述

实现代码

在这里插入图片描述
完整代码

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

void GetNext(char* p, int next[])
{
	int pLen = strlen(p);
	next[0] = -1;

	int k = -1;
	int j = 0;
	while (j < pLen - 1)
	{
		//p[k]表示前缀,p[j]表示后缀
		if (k == -1 || p[j] == p[k])
		{
			++k;
			++j;
			next[j] = k;
		}
		else
		{
			k = next[k];
		}
	}
}

int KmpSearch(char* s, char* p)
{
	int i = 0;	//主串中字符的移动标识i
	int j = 0;	//子串中字符的移动标识j

	int sLen = strlen(s);//主串字符个数
	int pLen = strlen(p);//子串字符个数

	int next[255];	//next数组,记录子串的移动规则
	GetNext(p, next);

	while (i < sLen && j < pLen)
	{
		//如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++
		if (j == -1 || s[i] == p[j])
		{
			i++;
			j++;
		}
		else
		{
			//如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]
			//next[j]即为j所对应的next值
			j = next[j];
		}
	}

	if (j == pLen)//在主串中找到子串
		return i - j;
	else
		return -1;//没找到返回-1
}

int main()
{
	//子串或模式串
	char p[20] = "ABCDABD";
	
	//主串
	char s[30] = "BBC ABCDAB ABCDABCDABDE";//23个字符

	printf("%d\n", KmpSearch(s, p));//15
	
	system("pause");
	return 0;
}

视频链接

KMP易懂版视频.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值