【数据结构】KMP算法概述

news/2024/10/17 12:24:00/

KMP算法,全称为Knuth-Morris-Pratt算法,是一种用于字符串匹配的算法。它的核心思想是利用已知信息来避免无用的比较操作,从而提高算法效率。KMP算法的时间复杂度为O(n + m),其中n为模式串的长度,m为文本串的长度。

本文将介绍基础的KMP算法和它的变种。

标准的KMP算法

KMP算法的基本思想是,当模式串中的某个字符与文本串中的某个字符匹配失败时,KMP算法利用已经匹配成功的信息,跳过一部分不必要的比较操作,直接从模式串的已匹配部分的下一个字符开始继续匹配。

下面我们来看一下KMP算法的实现代码。假设我们有一个文本串text和一个模式串pattern,要求判断text中是否包含pattern。

代码如下:

public static boolean kmp(String text, String pattern) {int n = text.length();int m = pattern.length();if (n < m) { // 特判,文本串长度小于模式串长度,直接返回falsereturn false;}// 计算next数组int[] next = getNext(pattern);// 从前往后匹配int i = 0, j = 0;while (i < n && j < m) {if (j == -1 || text.charAt(i) == pattern.charAt(j)) {i++;j++;} else {j = next[j]; // 跳过一部分不必要的比较操作}}return j == m; // 如果j等于m,说明匹配成功,返回true,否则返回false
}// 计算next数组
private static int[] getNext(String pattern) {int m = pattern.length();int[] next = new int[m];int i = 0, j = -1;next[0] = -1;while (i < m - 1) {if (j == -1 || pattern.charAt(i) == pattern.charAt(j)) {i++;j++;next[i] = j;} else {j = next[j];}}return next;
}

在上述代码中,我们先特判一下文本串长度小于模式串长度的情况,直接返回false。然后我们计算出模式串的next数组,接着从前往后遍历文本串和模式串,如果当前字符匹配失败,就从next数组中找到跳跃的位置,直接从模式串的已匹配部分的下一个字符开始继续匹配。最后如果模式串的所有字符都能匹配成功,则返回true,否则返回false。

下面给出一个示例来说明KMP算法的具体运作过程。假设我们要在文本串"ABCDABD"中查找模式串"ABD"。首先我们根据模式串计算出next数组,如下所示:

模式串ABD
next数组-100

接着我们开始遍历文本串和模式串,如下所示:

文本串ABCDABD
模式串ABD
next数组-100

显然,文本串中"A"和模式串中"A"匹配成功,继续比较下一个字符;文本串中"B"和模式串中"B"匹配成功,继续比较下一个字符;文本串中"C"和模式串中"D"匹配失败,此时我们根据next数组计算出跳跃的位置是0,所以直接从文本串的下一个字符"C"和模式串的第一个字符"A"继续开始匹配。

文本串ABCDABD
模式串ABD
next数组-100

此时,模式串中的"A"和文本串中的"A"匹配成功,继续比较下一个字符;模式串中的"B"和文本串中的"B"匹配成功,继续比较下一个字符;模式串中的"D"和文本串中的"D"匹配成功,说明模式串完全匹配文本串,KMP算法返回true,匹配成功。

通过这个例子,我们可以看到KMP算法利用了已知信息跳过了一部分不必要的比较操作,从而提高了算法效率。

值得注意的是,在计算next数组时,为了方便处理j == 0的情况,我们将next[0]初始化为-1。在匹配过程中,当j == -1时,说明模式串的第一个字符就与文本串中的当前字符匹配失败,因此直接将i和j同时+1即可。这一点需要特别注意。

综上所述,KMP算法是一种高效的字符串匹配算法,其核心思想是利用已知信息跳过不必要的比较操作,同时不会漏掉任何一个匹配。

变种KMP算法(Z算法)

除了上述基础的KMP算法,还有一种优化过的KMP算法,称为改进的KMP算法(也称Z算法)。

Z算法的核心思想是将原始的next数组优化为Z数组,从而避免了计算next数组时的回溯操作。

Z数组的计算过程较为复杂,这里不再赘述,有兴趣的读者可以自行了解。需要注意的是,Z数组的长度一定等于模式串的长度,因此需要将文本串和模式串拼接在一起,用特殊符号分隔开来,以避免越界。

下面是改进的KMP算法的代码实现:

public static boolean kmp(String text, String pattern) {int n = text.length();int m = pattern.length();char[] s = new char[n + m + 1];int[] z = new int[n + m + 1];text.getChars(0, n, s, 0);pattern.getChars(0, m, s, n + 1);s[n] = '#';int l = 0, r = 0;for (int i = 1; i <= n + m; i++) {if (i <= r) {z[i] = Math.min(z[i - l], r - i + 1);}while (i + z[i] <= n + m && s[i + z[i]] == s[z[i]]) {z[i]++;}if (i + z[i] - 1 > r) {l = i;r = i + z[i] - 1;}if (z[i] == m) {return true;}}return false;
}

在这个算法中,我们首先将文本串和模式串拼接在一起,并用特殊符号进行分隔,然后对拼接后的字符串计算Z数组(也称为Z函数)。Z数组z[i]表示以第i个字符为开头的子串与整个字符串的最长公共前缀长度(不包括第i个字符本身)。具体实现上,我们维护一个区间[l,r],表示已知的最长公共前缀的右端点r,左端点l即为当前i对应的区间左端点。如果i在[l,r]区间内,则可以利用已知信息避免无用的比较操作,直接计算z[i]的初始值;否则,需要从s[i]开始暴力匹配。如果z[i]达到了模式串的长度m,则说明匹配成功,返回true。

需要注意的是,在计算z[i]时,我们在检查s[i+z[i]]和s[z[i]]是否匹配时,应该首先判断i+z[i]是否在[l,r]区间内,这样才能利用已知信息避免无用的比较操作。

总的来说,改进的KMP算法相较于基础的KMP算法,在效率上有一定的提升,但实现起来也更为复杂。因此,在实际应用时需要根据具体情况选择适合的算法。


http://www.ppmy.cn/news/86676.html

相关文章

JNDI学习笔记

最近在研究JNDI注入漏洞&#xff0c;就先浅浅的学习以下JNDI相关知识。 JNDI对各种目录服务的实现进行抽象和统一化。 在 Java 应用中除了以常规方式使用名称服务(比如使用 DNS 解析域名)&#xff0c;另一个常见的用法是使用目录服务作为对象存储的系统&#xff0c;即用目录服务…

Spring Cloud(Kilburn 2022.0.2版本)系列教程(二) 服务消费者(RestTemplate+Loadbalancer)

Spring Cloud(Kilburn 2022.0.2版本)系列教程(二) 服务消费者(RestTemplate+Loadbalancer) 为了更好的浏览体验,欢迎光顾勤奋的凯尔森同学个人博客http://www.huerpu.cc:7000 一、服务消费 可以参考上节eurekaClientConsumer。 在启动类中,我们已经注入了一个restTemplate…

Linux音频和视频命令速查表

在Linux系统中&#xff0c;有许多命令可以帮助我们处理音频和视频文件&#xff0c;从基本的播放和转码&#xff0c;到编辑和处理音频、视频流。 本文将提供一个Linux音频和视频命令速查表&#xff0c;帮助您快速查找并了解各种常用的命令及其用法。 音频命令 播放音频文件 a…

超长溢出头部省略打点,坑这么大,技巧这么多?

目录 需求 利用 direction 实现头部超长溢出打点 简单介绍一下 direction&#xff1a; 另外两个与排版相关的属性还有&#xff1a; direction: rtl 会导致使用下划线 _ 连接的数字内容排版错误 多方案解决 方案一&#xff1a;两次 direction 反转 当然&#xff0c;这里…

【2023年第三届长三角高校数学建模竞赛】A 题 快递包裹装箱优化问题 20页完整论文及代码

相关链接 【2023年第三届长三角高校数学建模竞赛】A 题 快递包裹装箱优化问题 详细数学建模过程 1 题目 2022 年&#xff0c;中国一年的包裹已经超过 1000 亿件&#xff0c;占据了全球快递事务量的一半以上。近几年&#xff0c;中国每年新增包裹数量相当于美国整个国家一年的…

Yolov5涨点神器:注意力机制---多头上下文集成(Context Aggregation)的广义构建模块,助力小目标检测,暴力涨点

1.数据集性能验证 在crack道路缺陷检测任务中,多头上下文集成(Context Aggregation)的广义构建模块实现暴力涨点mAP50从0.954提升至0.992 🏆🏆🏆🏆🏆🏆Yolov5/Yolov7魔术师🏆🏆🏆🏆🏆🏆 ✨✨✨魔改网络、复现前沿论文,组合优化创新 🚀🚀🚀…

order by排序语句的用法

文章目录 学习连接语法用法示例1、按单个列的值排序2、按多个列的值排序3、按指定的规则排序4、按中文拼音字母顺序5、Order by和where条件共用 数据库中常用order by关键字对结果集进行排序&#xff0c;又可使用desc和asc来进行指定规则的排序。 学习连接 数据库&#xff1a;…

当你学会这项python数据提取神器时,请做好升职准备!

一、什么是 jsonpath ● JsonPath 是一种信息抽取类库&#xff0c;是从 JSON 文档中抽取指定信息的工具&#xff0c;提供多种语言实现版本&#xff0c;包括&#xff1a;JavaScript、Python、PHP 和 Java。 *文末领10节自动化精品课* 二、特点 ● 只能提取 JSON 格式的数据 ●…