最简单易懂《大话数据结构》KMP模式匹配算法next数组代码解析

问题描述

  最近在学习《大话数据结构》这本书,在看字符串这个章节的时候,书上讲述了KMP模式匹配算法来匹配字符串。
  在看了书上的描述后,相信很多人会跟我一样,对KMP算法有了一定的了解,也知道了原理。大家都知道KMP算法难点在于next数组的理解,我们可能知道通过一个字符串怎么一步步写出来next数组,但并不能理解书里的代码。为此我看了很多博客和视频,但是感觉很多人都是一笔带过,根本不解释为什么,所以自己今天写出来自己以前的疑惑点,供大家参考。

next数组公式

我们先来看看书上给的next数组的数学模型:
最简单易懂《大话数据结构》KMP模式匹配算法next数组代码解析_第1张图片
这个公式其实就是书上一步步推导next[j]得来的,比如对于这样一个字符串[a,b,a,b,a,a,a,b,a]:
最简单易懂《大话数据结构》KMP模式匹配算法next数组代码解析_第2张图片

  1. j=1时,next[1]=0,对应公式第1种情况;
  2. j=2时,1~j-1只有’a’一个字符,所以next[2]=1,对应公式第3种情况;
  3. j=3时,1~j-1是’ab’,没有前后缀相等,所以next[3]=1,对应公式第3种情况;
  4. j=4时,1~j-1是’aba’,前缀’a’和后缀’a’相等,next[4]=2,对应公式第2种情况;
  5. j=5时,1~j-1是’abab’,前缀’ab’和后缀’ab’相等,next[5]=3,对应公式第2种情况;
  6. j=6时,1~j-1是’ababa’,前缀’aba’和后缀’aba’相等,next[5]=4,对应公式第2种情况;
  7. j=7时,1~j-1是’ababaa’,前缀’a’和后缀’a’相等,next[5]=2,对应公式第2种情况;
  8. j=8时,1~j-1是’ababaaa’,前缀’a’和后缀’a’相等,next[5]=2,对应公式第2种情况;
  9. j=9时,1~j-1是’ababaaab’,前缀’ab’和后缀’ab’相等,next[5]=3,对应公式第2种情况;

  这是我们按照前后缀匹配来确定next, 可以看出来,这是与公式对应起来的,也就是说如果next[j]=k,对应于最前面的k-1个字符和第j个字符前的最后面k-1个字符相等相等,就是公式中的第二种情况。

原代码

接着,我们来看看书上给的代码,看看是怎么通过数学模型表示出来的。

/* 通过计算返回子串T的next数组。*/
void get_next(String T, int *next)
{
	int i,j;
	i=1;
	j=0;
	next[1]=0;
	while (i<T[0])		/*此处T[0]表示串的长度 */ 
	{
		if(j==0 || T[i]==T[j])	/* T[i]表示后缀的单个字符 */ 
								/* T[j]表示前缀的单个字符 */ 
		{
			++i;
			++j;
			next[i]=j;
		}
		else
			j=next[j];			/* 若字符不相同,则j值回溯 */ 
	}
 } 

代码中注释有T[i]表示后缀字符,T[j]表示前缀字符,这里有一个关系我们要知道就是:j实际上是next[i],也就是说前缀是next[后缀]
这段代码实际上就是递归求出next。初始条件是next[1]=0
这些大家应该都知道,这篇文章的重点是来解答两个地方:
最简单易懂《大话数据结构》KMP模式匹配算法next数组代码解析_第3张图片
下面我们就来看看这两步到底怎么来的。
目前已知i,j,和next[i](也就是j),如何求出next[i+1],这就是我们的目的
一、若T[i]==T[j]
此时因为next[i]已知为j,根据上面next的数学公式,我们知道j也就是next[i]满足下图,
在T[i]==T[j]的条件下,这个相等的字符串又变长了,变成了如下所示,

根据next的数学公式,这恰好满足条件二,也就是说next[i+1]=j+1,这就是代码中的第1个疑问的答案。
二、若T[i] != T[j]
这个时候我们知道相等的字符串已经是最大了,此时就需要回溯了,但是 j 要回溯到哪呢,为什么要回溯到 next[j] 那呢?
实际上我们需要在[1, j-1]中,缩减子串的长度,找到一个点k,使得[1, k-1]这个子串与[i - k+1, i - 1]子串相等,即:

那如何确定k值是多少呢?
我们观察这个子串会发现,其实k应该就是next[j],因为已知两个条件就是
最简单易懂《大话数据结构》KMP模式匹配算法next数组代码解析_第4张图片
注意,这里的 k=next[j] ,我们这一步是在证明k=next[j]是满足条件的
证明:
那么能否通过下面两个条件,得到上面我们要求的公式呢?
我们不妨将公式多写几个元素看看。
最简单易懂《大话数据结构》KMP模式匹配算法next数组代码解析_第5张图片
我们的目的就是要求画横线的两个地方相等,我们结合一张图来看,
最简单易懂《大话数据结构》KMP模式匹配算法next数组代码解析_第6张图片
蓝色虚线之间和粉色虚线之间相等,分别对应

红色、黄色、绿色、蓝色椭圆对应

我们根据k=next[j]知道红色黄色椭圆相等,由此而得知绿色蓝色椭圆也相等

我们的目的是求红色和蓝色椭圆相等

因为蓝色虚线之间和粉色虚线之间相等

得知红色绿色椭圆相等

由此,得出红色和蓝色椭圆相等。即:

那么,next[j]就是合理的k值,这刚好解释了第二个疑惑。

这篇文章写得特别的繁琐,就是希望大家在看的时候任何一处地方都不会有疑惑,但也难免造成了可读性特别差,自己的文笔不怎么好,大家将就着看。

你可能感兴趣的:(算法)