KMP算法详解

注:本文只讲解KMP怎么工作,不讲解KMP为什么这样工作。

KMP的介绍我不再进行赘述,现在就进入正题吧:

现在有以下两个字符串,我们想在文本串中找到与模式串一摸一样的子串。

在说到KMP如何工作之前,我们先了解一个东西:

最长公共前后缀

KMP算法实际上并不怎么关心文本串,我们所说的最长公共前后缀是特别针对模式串来说的,我们将上图所示的模式串分为以下子串:

A
AB
ABA
ABAB
ABABC

我们需要注意的是,在找最长公共前后缀的个数时,这个值必须小于当前子串的长度;

1.我们看第一个子串A,显然在这个串中没有与A一样的前后缀,同时A和A本身不能够算作公共前后缀(原因在前方标红了),所以这个子串的最长公共前后缀为0;

2.“AB”,很显然,仍然为0;

3.“ABA”,前一个A与后一个A相同,所以为1;

4“ABAB”,前面的AB,后面的AB(我们要找最长的,所以即使前A后A也相同我们也不要),为2;

5.“ABABC”,为0.

好了,于是我们得到了一个表格:

0 0120

关于两种next数组的形态

在KMP算法中,next数组是非常重要的,我们在比对的时候是要根据next数组决定指针该怎么移动。

在我观看不同的教程中发现,next数组形态主要有两种(不要怕,不难,接下来一起看看)

形态一:直接沿用上面的前缀表

上面由模式串中得出的一张表,我们直接作为next数组:

模式串ABABC
next数组00120

当我们比对文本串和模式串的时候,我们发现模式串最后一个字符匹配不上:

此时KMP算法会找到C对应的next数组值的前一个值——2,接下来,模式串将会发生这样的变化:

刚好,我们的模式串向后移动了两个位置,再度开始匹配。

我想提醒大家的一点是:实际上我们的字符串不会像这样移动,上述只是便于理解,真正起到作用的是指针,是指针的移动营造出了字符串移动的“假象”。

我们在文本串中给一个指针,模式串给一个指针,两个指针同时开始遍历,当文本串、模式串的指针遍历到上图位置时会发现此时比对的字符串不同,于是指针i查找next数组,跳到了下标为2的位置。

大家应当知道的是,我们需要比对的是两个指针所指向的和这之后的字符,而非之前的。

形态二:去尾添首

我们接着来说next数组的第二种形态。还记得之前的前后缀表吗?我们把这张表拿过来,将这张表的最后一个值去掉,在表的开头添上-1,于是我们得到:

模式串ABABC
next数组-10012
下标01234

OK,接下来的事情也是一样的;当指针遍历到(模式串)最后一个字符时,发现不匹配于是查找数组,发现对应数值是2,于是把下标为2的字符移动到当前位置——实际上仍然是指针跳到下标为2的字符上。

看完后你可能会产生的一些疑惑

如果模式串是类似于“AAA...”这样的格式,该如何找出最长公共前后缀呢?

其实也是一样的,如下图所示:

A0
AA1
AAA2

如果还是有点懵圈的小伙伴可以自己找一下。

当使用形态的二的next数组时,我们找到的数值是-1怎么办?

如果是在考试答题中,我相信大家都会自然而然的去画示意图,自然而然的去向后移动一位。

但是如果自己写代码的话,碰到0和-1有什么不一样呢?

当碰到0的时候,你肯定知道是把模式串中的指针移到0;那么仔细想想,如果碰到-1你还是只挪动模式串的指针(而且这时候指针肯定指向首元素),有什么意义呢?

这时候我们挪动的当然是文本串的指针,将其向后移动一位。

相信大家如果看懂这篇文章,一定能自己写出指针是如何移动的代码;当然,next数组是如何生成的,代码如何编写,就靠各位的智慧了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值