一篇优质的Knuth-Morris-Pratt算法讲解

2024-03-22 11:10

本文主要是介绍一篇优质的Knuth-Morris-Pratt算法讲解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 部分匹配表
    • 如何使用部分匹配表

部分匹配表

KMP的关键是部分匹配表。我和理解KMP之间的主要障碍是我没有完全理解部分匹配表中的值的真正含义。我现在将尽可能用最简单的话来解释它们。

下面是模式“ABABABACA”的部分匹配表:
在这里插入图片描述
如果我有一个八个字符的模式(在本例的持续时间内,让我们说“abababca”),我的部分匹配表将有八个单元格。如果我看表中的第八个也是最后一个单元格,我对整个模式感兴趣(“abababca”)。如果我看表中的第七个单元格,我只对模式中的前七个字符(“ababac”)感兴趣;第八个(“a”)是不相关的,可以从建筑物或其他东西上掉下来。如果我看到桌子上的第六个单元格,你会明白的。请注意,我还没有讨论每个单元格的含义,只是讨论了它所指的内容。
现在,为了讨论词义,我们需要知道正确的前缀后缀

正确前缀:字符串中的所有字符,末尾有一个或多个字符被截断。“S”、“Sn”、“Sna”和“Snap”都是“Snap”的正确前缀。

正确后缀:字符串中的所有字符,开头有一个或多个字符被截断。“agrid”、“grid”、“rid”、“id”和“d”都是“Hagrid”的适当后缀。

考虑到这一点,我现在可以给出部分匹配表中值的一句话含义:

(子)模式中与同一(子)模式中的正确后缀匹配的最长正确前缀的长度。

让我们来看看我的意思。假设我们在第三间牢房里。从上面你会记得,这意味着我们只对前三个字符(“aba”)感兴趣。在“aba”中,有两个适当的前缀(“a”和“ab”)和两个适当的后缀(“a”和“ba”)。正确的前缀“ab”与两个正确的后缀都不匹配。但是,正确的前缀“a”与正确的后缀“a”匹配。因此,在本例中,与正确后缀匹配的最长正确前缀的长度为1。

让我们在第四牢房试试。这里,我们对前四个字符(“abab”)感兴趣。我们有三个合适的前缀(“a”、“ab”和“aba”)和三个合适的后缀(“b”、“ab”和“bab”)。这一次,“ab”在两者中,有两个字符长,所以第四单元的值为2。

因为这是一个有趣的例子,让我们也在涉及“ababa”的第五单元中尝试一下。我们有四个适当的前缀(“a”、“ab”、“aba”和“abab”)和四个适当的后缀(“a”、“ba”、“aba”和“baba”)。现在,我们有两个匹配项:“a”和“aba”都是正确的前缀和后缀。因为“aba”比“a”长,所以它获胜,第五单元的值为3。

让我们跳到第七单元(倒数第二个单元),它与模式“ababac”有关。即使没有列举所有正确的前缀和后缀,也应该很明显不会有任何匹配项;所有后缀都将以字母“c”结尾,没有一个前缀将以字母“c”结尾。由于没有匹配项,第七单元得到0。

最后,让我们看看第八单元,它涉及整个模式(“阿巴巴卡”)。由于它们都以“a”开头和结尾,我们知道该值至少为1。然而,这就是它的终点;长度为2及以上时,所有后缀都包含c,而只有最后一个前缀(“ababac”)包含c。这个七个字符的前缀与七个字符的后缀(“bababca”)不匹配,所以第八单元得到1。

如何使用部分匹配表

当我们发现部分匹配时,可以使用部分匹配表中的值提前跳过(而不是重复不必要的旧比较)。公式的工作原理如下:

如果找到长度为partial_match_length的部分匹配,并且表[partial_match_length]>1,我们可以跳过partial_match_length-表[partial_match_length-1]字符。

假设我们将模式“abababca”与文本“bacbabab”进行匹配。这是我们的部分匹配表,便于参考:
在这里插入图片描述

我们第一次得到部分匹配是在这里:
在这里插入图片描述
This is a partial_match_length of 1. The value at table[partial_match_length - 1] (or table[0]) is 0, so we don’t get to skip ahead any. The next partial match we get is here:
在这里插入图片描述

这是5的部分匹配长度。表[partial_match_length-1](或表[4])中的值为3。这意味着我们可以提前跳过部分匹配长度表[partial\u match\u length-1](或5表[4]或5-3或2)字符:
在这里插入图片描述
这是3的部分匹配长度。表[partial_match_length-1](或表[2])中的值为1。这意味着我们可以提前跳过部分匹配长度表[部分匹配长度-1](或3表[2]或3-1或2)字符:
在这里插入图片描述此时,我们的模式比文本中的其余字符长,因此我们知道不存在匹配。

这篇关于一篇优质的Knuth-Morris-Pratt算法讲解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/835180

相关文章

Python Transformers库(NLP处理库)案例代码讲解

《PythonTransformers库(NLP处理库)案例代码讲解》本文介绍transformers库的全面讲解,包含基础知识、高级用法、案例代码及学习路径,内容经过组织,适合不同阶段的学习者,对... 目录一、基础知识1. Transformers 库简介2. 安装与环境配置3. 快速上手示例二、核心模

openCV中KNN算法的实现

《openCV中KNN算法的实现》KNN算法是一种简单且常用的分类算法,本文主要介绍了openCV中KNN算法的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录KNN算法流程使用OpenCV实现KNNOpenCV 是一个开源的跨平台计算机视觉库,它提供了各

C++ vector的常见用法超详细讲解

《C++vector的常见用法超详细讲解》:本文主要介绍C++vector的常见用法,包括C++中vector容器的定义、初始化方法、访问元素、常用函数及其时间复杂度,通过代码介绍的非常详细,... 目录1、vector的定义2、vector常用初始化方法1、使编程用花括号直接赋值2、使用圆括号赋值3、ve

Java调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++

springboot+dubbo实现时间轮算法

《springboot+dubbo实现时间轮算法》时间轮是一种高效利用线程资源进行批量化调度的算法,本文主要介绍了springboot+dubbo实现时间轮算法,文中通过示例代码介绍的非常详细,对大家... 目录前言一、参数说明二、具体实现1、HashedwheelTimer2、createWheel3、n

Python基础文件操作方法超详细讲解(详解版)

《Python基础文件操作方法超详细讲解(详解版)》文件就是操作系统为用户或应用程序提供的一个读写硬盘的虚拟单位,文件的核心操作就是读和写,:本文主要介绍Python基础文件操作方法超详细讲解的相... 目录一、文件操作1. 文件打开与关闭1.1 打开文件1.2 关闭文件2. 访问模式及说明二、文件读写1.

C# WinForms存储过程操作数据库的实例讲解

《C#WinForms存储过程操作数据库的实例讲解》:本文主要介绍C#WinForms存储过程操作数据库的实例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、存储过程基础二、C# 调用流程1. 数据库连接配置2. 执行存储过程(增删改)3. 查询数据三、事务处

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

C++快速排序超详细讲解

《C++快速排序超详细讲解》快速排序是一种高效的排序算法,通过分治法将数组划分为两部分,递归排序,直到整个数组有序,通过代码解析和示例,详细解释了快速排序的工作原理和实现过程,需要的朋友可以参考下... 目录一、快速排序原理二、快速排序标准代码三、代码解析四、使用while循环的快速排序1.代码代码1.由快