【数据结构与算法】->算法->字符串匹配基础(下)->KMP 算法

本文详细介绍了KMP算法,它是最知名的字符串匹配算法之一,与BM算法本质相似。KMP算法的核心是利用模式串的好前缀和最长可匹配后缀子串,通过预先计算的`next`数组(失效函数)来避免不必要的字符比较,提高匹配效率。文章首先回顾了字符串匹配的基础,并引入KMP算法的基本原理,接着解释了如何计算`next`数组,最后分析了算法的时间复杂度为O(m+n)。
摘要由CSDN通过智能技术生成

Ⅰ 前言

在前两节中,我详细讲了字符串匹配的三个算法,BF,RK 和 BM 算法,BM 算法可以说是这三个包括这篇要讲的 KMP 算法里最难最复杂的,也非常不好理解。但是 BM 算法却是工程中非常常用的一种高校字符串匹配算法。有统计说,它是最高效、最常用的字符串匹配算法。但是,在所有的字符串匹配算法中,要说最知名的一种,那肯定是非 KMP 算法莫属。很多时候,提到字符串匹配,我们首先想到的就是 KMP 算法。

尽管在实际开发中,我们几乎不太可能自己亲手实现一个 KMP 算法。但是,学习这个算法的思想,作为让你开拓眼界、锻炼下逻辑思维也是很不错的。

实际上,KMP 算法跟 BM 算法的本质是一样的。在 BM 算法中,我讲了好后缀和坏字符规则,现在我们就来借助 BM 算法的思路,来理解 KMP 算法。

如果你对 BM 算法还不了解,请跳转去看我下面的文章。

【数据结构与算法】->算法->字符串匹配基础(中)->BM算法->KMP 三倍性能的强大算法

【数据结构与算法】->算法->字符串匹配基础(上)->BF 算法 & RK 算法

Ⅱ KMP 算法基本原理

KMP 算法是根据三位作者(D.E.Knuth,J.H.Morris,Knuth Morris Pratt)的名字来命名的,算法的全称是 Knuth Morris Pratt 算法,简称 KMP 算法。

KMP 算法的核心思想,和 BM 算法非常相近。我们假设主串是 a,模式串是 b。在模式串与主串匹配的过程中,当遇到不可匹配的字符的时候,我们希望找到一些规律,可以将模式串往后多滑动几位,跳过那些肯定不会匹配的情况。

在 BM 算法中,我讲了好后缀和坏字符。这里我们可以类比一下,在模式串和主串匹配的过程中,把不能匹配的那个字符仍然叫作坏字符,把已经匹配的那段字符串叫作好前缀

在这里插入图片描述
当遇到坏字符的时候,我们就要把模式串往后滑动,在滑动的过程中,只要模式串和好前缀有上下重合,前面几个字符的比较,就相当于拿好前缀的后缀子串,跟模式串的前缀子串在比较。这个比较过程能不能更高效一点?可不可以不要一个字符一个字符地比较?

KMP 算法就是在试图寻找一种规律:在模式串和主串匹配的过程中,当遇到坏字符后,对于已经比对过的好前缀,能否找到一种规律,将模式串一次性滑动很多位。

我们只需要拿好前缀本身,在它的后缀子串中,查找最长的那个可以跟好前缀的前缀子串匹配的。假设最长的可匹配的那部分前缀子串是 {v},长度是 k。我们把模式串一次性往后滑动 j-k 位,相当于,每次遇到坏字符的时候,我们就把 j 更新称 k,i 不变,然后继续比较。

在这里插入图片描述
为了表述起来方便,我把好前缀的所有后缀子串中,最长的可匹配前缀子串的那个后缀子串,叫作最长可匹配后缀子串;对应的前缀子串,叫作最长可匹配前缀子串

在这里插入图片描述
如何来求好前缀的最长可匹配前缀和后缀子串呢?这个问题其实不涉及主串,只需要通过模式串本身就能求解。这和 BM 算法的思路是一致的,因为一个好前缀或者 BM 算法中的好后缀,都是主串和模式串共有的,所以我们都要对模式串进行预处理,以便提高代码效率。

类似 BM 算法中的 badCharsuffixprefix数组,KMP 算法也可以提前构建一个数组,用来存储模式串中每个前缀(这些前缀都有可能是好前缀)的最长可匹配前缀子串的结尾字符下标。我们把这个数组定义为 next数组,很多书中还给这个数组起了一个名字,叫失效函数(failure function)

数组的下标是每个前缀结尾字符下标,数组的值是这个前缀的最长可以匹配前缀子串的结尾字符下标。这句话比较拗口,我举一个例子,大家对着看,差不多就能理解了。

在这里插入图片描述
我还是详细地说一下这些值是怎么来的。

左边的表头为模式串前缀的这列,意思是把它们当作好前缀。因为我们现在是根据模式串来处理主串中的好前缀问题,所以我们把每个前缀都当作是好前缀,然后根据这个好前缀,来计算它的最长可匹配前缀子串结尾字符下标。

所以当好前缀是 a 的时候,它就没有前缀,更不会有可匹配前缀子串,所以 next[0] = -1;

当好前缀是 ab 的时候,它只有一个前缀子串 a,和它的后缀子串 b 不匹配,所以 next[1] = -1;

当好前缀是 aba 的时候,它的一个前缀是 a,和它的后缀 a 是匹配的;它的前缀 ab 和后缀 ba 不匹配,所以它的最长可匹配前缀子串就是 a,下标为 0,所以 next[2] = 0;

以此类推,next 数组的值就是这样计算的。有了这个数组,有很好实现 KMP 算法了。我们先假设 next 数组已经计算好了,直接可以用,根据这个我们给出 KMP 算法的框架。

	/**
	 * KMP算法实现字符串匹配
	 * @param mainString 主串
	 * @param patternString 模式串
	 * @return 模式串在主串中的位置
	 */
	public static int kmp(char[] mainString, char[] patternString) {
   
		int[] next = getNexts(patternString);
		int j = 0;
		for (int i = 0; i < mainString.length; i++) {
   
			while (j > 0 && mainString[i] != patternString
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值