KMP算法学习记录

KMP算法学习记录
参考:
https://blog.csdn.net/qq_62982856/article/details/128003067
https://blog.csdn.net/raelum/article/details/128823560
https://www.zhihu.com/question/21923021/answer/281346746
https://blog.csdn.net/qq_37969433/article/details/82947411

一、字符串基础

1️⃣ 字符串的前缀是指不包含最后一个字符的所有以第一个字符(索引为0)开头的连续子串

比如字符串 “ABABA” 的前缀有:A,AB,ABA,ABAB

2️⃣ 字符串的后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串

比如字符串 “ABABA” 的后缀有:BABA,ABA,BA,A

3️⃣ 公共前后缀:一个字符串的所有前缀连续子串所有后缀连续子串中相等的子串

比如字符串 “ABABA”

  • 前缀有:A,AB,ABA,ABAB
  • 后缀有:BABA,ABA,BA,A

因此公共前后缀有:A ,ABA

4️⃣ 最长公共前后缀:所有公共前后缀的长度最长的 那个子串

比如字符串 “ABABA” ,公共前后缀有:A ,ABA

由于 ABA 是 三个字符长度,A 是一个字符长度,那么最长公共前后缀就是 ABA

原文链接:https://blog.csdn.net/qq_62982856/article/details/128003067

二、KMP算法

构造 next 数组,保持原串的 i 指针不动,然后将匹配串的 j 指针指向模式字符串的 next[j] 位即可。

1. next 数组构建思想

next数组由 部分匹配表(Partial Match Table) 得到

我觉得理解KMP的最大障碍就是很多人在看了很多关于KMP的文章之后,仍然搞不懂PMT中的值代表了什么意思。这里我们抛开所有的枝枝蔓蔓,先来解释一下这个数据到底是什么。

对于字符串“abababca”,它的PMT如下表所示:就像例子中所示的,如果待匹配的模式字符串有8个字符,那么PMT就会有8个值。

PMT中的值是字符串的前缀集合与后缀集合的交集中最长元素的长度。

例如,对于”aba”,它的前缀集合为{”a”, ”ab”},后缀集合为{”ba”,”a”}。两个集合的交集为{”a”},那么长度最长的元素就是字符串”a”了,长度为1,所以对于”aba”而言,它在PMT表中对应的值就是1

再比如,对于字符串”ababa”,它的前缀集合为{”a”, ”ab”,”aba”, ”abab”},它的后缀集合为{”baba”, ”aba”, ”ba”, ”a”}, 两个集合的交集为{”a”,”aba”},其中最长的元素为”aba”,长度为3。

如图所示,要在主字符串"ababababca"中查找模式字符串"abababca"。如果在 j 处字符不匹配,那么主字符串中 i 指针之前的 PMT[j −1] 位就一定与模式字符串的第 0 位至第 PMT[j−1] 位是相同的。这是因为主字符串在 i 位失配,也就意味着主字符串从 i−j 到 i 这一段是与模式字符串的 0 到 j 这一段是完全相同的

模式字符串从 0 到 j−1 ,在这个例子中就是”ababab”,其前缀集合与后缀集合的交集的最长元素为”abab”, 长度为4。所以就可以断言,主字符串中i指针之前的 4 位一定与模式字符串的第0位至第 4 位是相同的,即长度为 4 的后缀与前缀相同。这样一来,我们就可以将这些字符段的比较省略掉。具体的做法是,保持i指针不动,然后将j指针指向模式字符串的PMT[j −1]位即可
在这里插入图片描述
以图中的例子来说,在 i 处失配,那么主字符串和模式字符串的前边6位就是相同的。又因为模式字符串的前6位,它的前4位前缀和后4位后缀是相同的,所以我们推知主字符串i之前的4位和模式字符串开头的4位是相同的。就是图中的灰色部分。那这部分就不用再比较了。

为了编程的方便, 我们不直接使用PMT数组,而是将PMT数组向后偏移一位。我们把新得到的这个数组称为next数组

原文链接:https://www.zhihu.com/question/21923021/answer/281346746

2. next 数组的构建

假设有匹配串 aaabbab,我们来看看对应的 next 是如何被构建出来的。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
最后,往原串和匹配串头部追加一个空格(哨兵)。目的是让 j 下标从 0 开始,省去 j 从 -1 开始的麻烦。第0位的值,我们将其设成了-1。
原文链接:https://leetcode.cn/problems/find-the-index-of-the-first-occurrence-in-a-string/solutions/575568/shua-chuan-lc-shuang-bai-po-su-jie-fa-km-tb86

举例

在这里插入图片描述

三、代码

求next数组值的程序如下所示:

void getNext(char * p, int * next)
{
	next[0] = -1;
	int i = 0, j = -1;

	while (i < (int)strlen(p))
	{
		if (j == -1 || p[i] == p[j])
		{
			++i;
			++j;
			next[i] = j;
		}	
		else
			j = next[j];
	}
}

具体KMP代码如下(示例):

nt KMP(char * t, char * p) 
{
	int i = 0; 
	int j = 0;

	while (i < (int)strlen(t) && j < (int)strlen(p))
	{
		if (j == -1 || t[i] == p[j]) 
		{
			i++;
           	j++;
		}
	 	else 
           	j = next[j];
    	}

    if (j == strlen(p))
       return i - j;
    else 
       return -1;
}

原文链接:https://www.zhihu.com/question/21923021/answer/281346746

  • 17
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

大田斗小木子

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

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

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

打赏作者

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

抵扣说明:

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

余额充值