数据结构与算法基础(王卓)(16):KMP算法(个人学习历程)
创始人
2024-06-03 01:33:02
0

如果只是想快速了解上手KMP,可以直接看:

数据结构与算法基础(王卓)(17):KMP算法详解(精讲(最简单、直接、有效的思路方法)不含代码,但说明了所有代码怎么写(原理))_宇 -Yu的博客-CSDN博客


关于next [ j ]

根据书上以及视频上给出的思路(提醒),我们对于KMP算法拥有了如下的初步(第一阶段)的了解:

书上的内容(经过简化和解释说明后的版本):

分析模式串t:

对于模式串(子串)t的每个字符 t [ j ]  (0≤j≤m-1)

即 j 在字符串最后一个字符前

存在一个整数k(k

模式串中开头的k个字符(t0…t[ k-1])

依次与t[ j ]的

前面k个字符(t[ j – k ]…t[ j – 1 ])相同

其实就是说:

子串里面的第j个字符,这个字符他前面的k个字符刚好和子串最前面(开头)的k个字符一模一样

注:

这里,我们暂且就给这两串相同的玩意取个名字方便称呼:

我们将前者称之为前缀,后者称之为后缀

将其图像化可能更加直观:

这种属性落实到具体提高比较效率上,重点就是:当出现了前缀和后缀以后

我们可以把(子串)前缀移动到后缀的位置,主串不变,进行下一轮比较

换句话说,就是在(到)下一轮比较时

直接把前缀移动到(移动)之前后缀所处的位置,跳过这中间所有的字符

直接进行这个位置开始的,后面的比较

学习过程中遇到的问题:

按理说,这里接下来我们就可以进行顺理成章地归纳关于next [ i ]的公式了,比如说至少能理解书上的这一条:

 但是,这里我们很容易就发现一个问题:

不是,你这个子串不是说是要往后移吗,怎么经过了这个公式怎么还越变越小了???

k都移动到第j位了,j 不得移动到 j +(j-k)位上???

这个大概就不对了吧?又或者说,next [ j ]其实并不代表下一次 j 的位置?


然而实际上,该问题的出现根源于没有真正的画图和敲代码(实践)

而该具体问题的核心在于:

 j 往前指(指向字符串前面的第k个字符)

并不是说

让 子串的 位序为 j 的 字符移动到 主串的 位序为next [ j ]的位置(正下方)开始匹配

把后缀移动到之前前缀的位置上来

另外,在这个算法案例中此 j (next【j】)非彼 j(前面文字介绍里面的 j) 

这里的 j, 相当于一个功能类似于指针的一个下标


要彻底搞清楚该问题过程的核心和本质,我们需要彻底从头开始,重新缕一缕这个KMP算法

(再整个比较过程中的流程和步骤):

实践操作步骤:

  1. 直接匹配(一个一个字符往后匹配),直到匹配不上
  2. 看匹配不上的字符之前的字符有没有能实现前缀后缀一样的
  3. (有一样的话)直接把前缀移动到后缀之前摆放的位置
  4. 继续匹配

这里 j 的执行过程是

从t【0】开始往后面排,匹配发现不一样以后,i 不变,j(不一样的前一位)

数值变为next [ j ],指向子串内下标为next [ j ] 的字符

再次说明强调:

不是说让 子串的 位序为 j 的 字符移动到 主串的 位序为next [ j ]的位置(的正下方)开始匹配

是主串不动,j 指向子串内下标为next [ j ] 的字符

相当于将子串内下标为next [ j ] 的字符向后移动到原来下标为 j 的字符的位置

注意:

这里写的所谓的“移动”的说法,只是我们为了方便初步理解匹配算法的过程

实际上并不存在什么子串的移动来移动去,只有说:

操作过程前,主串的同一个字符(位序为 i ),比较的是子串里(相对而言)靠前面的字符(位序为 j )

操作后,主串的同一个字符(位序为 i ),比较的是子串里(相对而言)靠后面的字符(位序为 next [ j ] )


关于next [ j ]的总结:

解决了这么大的一个问题,现在,我们终于可以可以归纳关于next [ i ]的公式了:

(1):如上面所示,如果存在前缀后缀相同的情况,我们可以让 j (可移动的类似指针的)下标变为 k (指向子串中位序为k的,前缀的后面的第一位字符)来加速比较

(2):上面我们都默认下标(位序) j 是从0开始,是因为我们的书上写的都是默认为0的情况

实际上下标可以从0开始,也可以从1开始(比如说PPT、网课里面)

但是

对于第一位下标的 next [ j ]  值,他们都选择了:

比第一个下标小1位(第一个下标的前面一位,也是我们实际上永远都取不到的一个位置)

对于“其他情况”(不是第一位但是也没有什么相同的前缀和后缀)的 next [ j ]  值

他们都选择了:第一个下标位

所以说实际上都可以,表面上两个归纳的结果的数值完全不一样

PPT(网课)上的归纳公式

书上的公式

实际上他们的数值制定的原理本质都是一样的,似非而是

而实际上两个公式最终产生的结果,也就是所有结果都相差一个位置,仅此而已

而在这里为了应用的方便,我们统一都采用(写成)书上(从0开始)的形式

但是我们也要知道:

如果我们不想从0开始,想要从1开始,这也都是可以的,只要直接按照PPT上面所执行的公式操作就行


next 代码思路:

那么接下来,就是我们把准备了那么多的时间的思想转换为代码的时候了:


框架

首先,我们先把整个(KMP)匹配算法的大框架搭建好:

int Index_KMP(SString S, SString T, int pos)
{int i = pos, j = 0;while (i <= S.length && j <= T.length){if (S.ch[i] == T.ch[j]){++i; ++j;}//主串和子串依次匹配下一个字符elsej = next[j];		}if (j > T.length) return i - T.length; //匹配成功,返回子串位置else return false;
}

难题:如何写出一个判断子串的前后缀是否相同的语句

另外在这里,一开始其实我想写的是不用写什么next【j】,直接在代码里通过算法实现倒退到next【j】的功能,但是这样反而有点混乱,逻辑不清,而且到后面其实已经写不下去了:

			int k = 0;while (1){if (T.ch[k] == T.ch[j]){k++; j--;//然后写一个判断子串的前后缀是否相同的语句//但是这里这样写的话我们可以说要写无穷个判断语句//根本无法实现}}

                    //然后写一个判断子串的前后缀是否相同的语句
                    //但是这里这样写的话我们可以说要写无穷个判断语句
                    //根本无法实现

所以,如何写出一个判断子串的前后缀是否相同的语句使该算法的核心/重点

下面我们来针对此方面开展工作


首先,我们按部就班根据公式:

 写出如下程序:

void Get_next(SString T, int(&next)[])
//给你一个子串T,教你逐个算出每个位序对应的next[]
//&:返回所有我们算出的next[]
{int j = 0,//从头开始算起k = -1;//		k = 0; //不可以,根据公式和算法设计,即使是MAX[k]也必须要小于jnext[0] = -1;//根据公式while (j <= T.length - 1)//因为位序从0(而非1)开始{if (k == -1 || T.ch[k] == T.ch[j]){}}
}

然而写到具体如何一个一个判断匹配把比较前缀后缀的思想实现成代码的时候又卡壳卡住了

对此,我们的解决方法是:

多画图,一步一步、一格一格算,不用着急,慢慢来

画出步骤图如下:

 

在这个过程中,我们很容易就感受到:

其实如果上一步进行匹配运算结果为真的话

下一轮其实我们只需要比较上一轮比较的两个串的后面的一个字符就可以直接判定结果

下一轮的next 【j】是不是上一轮加一

有人说你这TM不是废话吗,但是这句废话在我们这里的程序设计中含有至关重要的意义:

事实上,根据上面这句废话,我们可以画出我们在采用这种方法的流程图

如下:

 根据上述流程图,我们不难得到:


if情况:(新字符匹配)

(1):实现前面所说的

实现一个一个判断匹配把比较前缀后缀的思想实现成代码的操作

至少这里我们可以通过废话:

其实如果上一步进行匹配运算结果为真的话

下一轮其实我们只需要比较上一轮比较的两个串的后面的一个字符就可以直接判定结果

下一轮的next 【j】是不是上一轮加一

以及流程图写出 if 判断语句后面的表达式:

思考逻辑流程:

第一次给next【j】赋值的时候
我们要意识到next【0】是在一开始我们就已经给了他初值的
也就是说第一个被赋值的,是next【1】

此时(系统给的条件): j = 0, k = -1;  而我们要写入的,是next【1】


重新参考步骤图,我们知道:

给next【1】赋值时,k = 0 ,j = 1;

更何况后面每一步我们都要进行自增,然后再比较的操作

所以自然的:

            j++;
            k++;

的操作是不可少的


然后我们要考虑的就是赋值和自增操作的前后顺序安排问题了:

再对应着步骤图一个一个看:

next [ 1 ] k = 0 ,j = 1next [ 1 ] = 0;
next [ 2 ] k = 1 ,j = 2

一样:next [ 2 ] = 1

不一样:next [ 2 ] = 0

next [ 3 ] k = 2 ,j = 3

一样:next [ 3 ] = 2

不一样:next [ 3 ] = 1或0

我们可以看到:

如果(后面的那一个新的字符)匹配结果为真

则 next [ j ] 的值,就为新的(和上一轮不一样的)k的值(新的值其实这里也就是自增过以后得的值)

匹配结果为假,那是else情况里面的东西,我们先不管他

所以从上述操作我们大概就可以判断出来:如果(后面的那一个新的字符)匹配结果为真

那么就先自增,然后赋值next [ j ] = 新的k


else情况:(新字符不匹配)

现在,我们回过头去看看流程图,研究匹配结果为假的情况:

步骤:

(1):我们会(可以)发现,无一例外,他们进行的操作都是去执行少一位的前缀和后缀的比较(匹配)的算法操作

(当然了,很多人会说,这又是一句废话,你TM介绍算法的基本原理的时候里面TM不就写着吗)

(2):既然如此(他执行的还是这个比较的操作),比较的操作流程必须由前面的 if 语句执行:

一方面我不可能去再重复写一遍这个比较

另一方面如果一直这样写下去的话,后面就变成了无穷无尽的循环了

所以说到这一步,我们需要思考的:

怎么让前缀和后缀这两个东西倒(回退)回去,而不是在 else 里面写比较的语句

(3):在参考过课本上的思路以后,我们意识到:

其实回头去找前缀后缀里面最长的、能相等的两个串,本质上和我们比较子串和主串本质上其实没什么区别

也就是说,在这里,我们可以用KMP算法直接加速这一比较的过程

更巧的是,他前面其实已经给我们算好了 j 前面所有的 next [ j ]

当然,在这我写是这么写,但是总感觉要完整这个过程好像还缺点什么,不够确定就是这样(说不出来缺了什么东西)

总的来说,到了这一步,我们可以用KMP算法,在 else 语句后面写:

            k = next[k];

也可以老老实实的就用BF算法:

            k--;

是 k-- 吗?好像不确定

事实上,这里我们对KMP还是属于一知半解的状态,所以后面学习了

DS第四章【3】KMP1_哔哩哔哩_bilibili

整理出

数据结构与算法基础(王卓)(17):KMP算法详解(精讲(最简单直接有效的思路方法))_宇 -Yu的博客-CSDN博客

以后才彻底理解

相关内容

热门资讯

安卓系统为啥不更新了,安卓系统... 你有没有发现,最近你的安卓手机好像有点儿“懒”了,更新系统的时候总是慢吞吞的,甚至有时候直接告诉你:...
安卓系统手电在哪里打开,安卓手... 你有没有在安卓手机上找过手电筒功能,结果却像在茫茫大海中捞针一样?别急,今天就来给你详细揭秘,安卓系...
安卓系统个版本通用吗 你有没有想过,你的安卓手机上的系统版本是不是和别人的手机一样通用呢?这个问题听起来可能有点奇怪,但确...
安卓玩鸿蒙系统好吗,安卓用户玩... 你有没有想过,把安卓手机换成鸿蒙系统,会是怎样的体验呢?最近,这个话题在互联网上可是掀起了一阵热潮。...
安卓开源报名系统源码,架构设计... 你有没有想过,那些在手机上报名参加各种活动、课程或者考试的安卓应用,其实背后有一个神秘的“大脑”——...
峰米系统是安卓系统吗,揭秘安卓... 你有没有听说过峰米系统?最近这个话题在数码圈里可是挺火的。很多人都在问,峰米系统是安卓系统吗?今天,...
华为系统emui和安卓系统的区... 你有没有发现,手机的世界里,系统就像是它们的灵魂,决定了它们能跳得多高、飞得多远。今天,咱们就来聊聊...
朗逸安卓系统车载导航,安卓系统... 你有没有想过,开车的时候,导航系统就像是个贴心的导航小精灵,带你穿梭在城市的每一个角落?今天,就让我...
排球鹰眼系统辅助器安卓,安卓排... 你有没有想过,在观看排球比赛时,那些精准的鹰眼系统是如何让比赛画面更加清晰、精彩呢?现在,就让我带你...
安卓系统和magic的区别 你有没有发现,现在手机市场上安卓系统和Magic系统就像是一对双胞胎,长得有点像,但又各有各的特色。...
安卓系统模拟win7,轻松实现... 你有没有想过,在安卓手机上也能体验Windows 7的韵味呢?没错,这就是今天我要跟你分享的神奇之旅...
安卓系统查公交车 你有没有想过,在繁忙的都市生活中,如何轻松地查找到你心仪的公交车呢?别急,今天就来给你支个招——利用...
强悍的安卓10系统下载,强悍性... 你有没有听说最近安卓系统又升级啦?没错,就是那个强悍的安卓10系统!你是不是已经迫不及待想要体验一下...
安卓不开机怎么重置系统,轻松恢... 手机突然不开机了,是不是心里慌得一批?别急,今天就来教你一招,让你的安卓手机重置系统,恢复活力! 一...
安卓怎么移植到苹果系统,揭秘跨... 你是不是也和我一样,手里拿着安卓手机,却对那苹果系统的诱惑无法抗拒?想要把安卓上的宝贝应用和资料搬到...
安卓9系统特点在哪,系统革新与... 你有没有发现,你的安卓手机最近是不是变得有点不一样了?没错,就是那个默默无闻的安卓系统,它悄悄地升级...
苹果手机系统操作安卓版,轻松迁... 你有没有想过,为什么苹果手机的系统那么受欢迎,而安卓版却总是被大家调侃呢?今天,就让我带你从多个角度...
麦芽安装安卓系统怎么安装,轻松... 你有没有想过,把安卓系统装到麦芽上,是不是就像给老式收音机配上蓝牙功能那么酷炫呢?想象你的麦芽设备瞬...
直板安卓系统手机游戏,直板安卓... 你有没有发现,最近手机游戏界又掀起了一股热潮?没错,就是那些让你一玩就停不下来的直板安卓系统手机游戏...
nolia是安卓系统吗,安卓系... 你有没有听说过Nolia这个操作系统?最近它在科技圈里可是小火了一把呢!很多人都在问,Nolia是安...