百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

一文搞定KMP字符串匹配算法

wxin55 2024-11-17 16:47 8 浏览 0 评论

我一直坚信一点,就是算法不分好和坏,只要能解决问题的算法都是好算法,有一句俗话,叫是骡子是马拉出去溜溜,算法也是一样的,你能解决问题,那这个算法就是好算法,算法的适用场景是不一样的,非常复杂的算法它在小型的设备上就会受到局限性,反而简单一些的算法会受到欢迎,今天呢,就跟大家聊聊KMP字符串匹配算法,很多新手看这个算法,比较蒙圈,我今天就将这个算法讲解透彻,有兴趣的同学可以看看,多多关注我,给我更大的动力。

现在给你一篇文章,在文章中,找到你想要的内容,这就是字符串匹配,那么这里就涉及到如何查找的问题,我们假设文章的内容为”abcdefgabcabc”,现在我们需要在这个串中,查找是否存在”abcabc”这个子串,对于很多人,都能想到一种算法,我们委婉的说一下,就是朴素的匹配算法,见下面的示意图,一目了然,请看下面:

1. 最直接的匹配算法(朴素的匹配算法)

1) 经过比较我们发现,主串S的第一到第三个字符和匹配串T的前三个字符对应上了,但是第四个字符没有和T的第四个字符匹配上,那么继续匹配。

2) 主串从第二个字符b开始,和T进行匹配,发现b和a不相等,继续下一轮的匹配。

3) 主串从第三个字符c开始与T进行匹配,发现c和a不相等,继续下一轮的匹配。

4)主串的第四个字符d开始和T进行匹配,发现d和a不相等,继续下一轮的匹配。

5)主串的第五个字符e和T进行匹配,发现e和a不相等,继续下一轮的匹配。

6)主串的第六个字符f和T进行匹配,发现f和a不相等,继续下一轮的匹配。

7)主串的第七个字符g和T进行匹配,发现g和a不匹配,继续下一轮的匹配。

8)主串的第八个字符直到末尾和T进行了全匹配,全部相等,匹配成功。

经过上面的匹配流程,我们知道了朴素的匹配算法的效率是比较低效的,每次和匹配串T匹配不上的时候,就从主串的下一个字符开始和T进行匹配,那么我们发现,匹配失败后,变的是主串的位置,匹配串T始终从首字符和主串进行匹配,这种效率极其低下,也就是出现了大量重复的匹配,最坏的情况是把主串全部遍历一遍,这个你能忍受的了吗?我是忍受不了。

所以,出现了KMP的匹配算法,KMP是对朴素算法的一种改进,其实思想非常简单,我来一一说明。

2. KMP匹配算法

有三位前辈,D.E.Knuth、J.H.Morris和 VR.Pratt共同研究,针对朴素匹配算法进行了改进,提出了KMP的匹配算法。

KMP算法就是针对大量重复匹配而提出的一种改进算法,避免了重复匹配的过程,那么下面我们来研究一下KMP是如何避免重复匹配的呢?

我们可以看到,主串S和子串T在匹配过程中,d字符和a字符没有匹配上,匹配上的是abc这三个字符,按照朴素算法,会用主串的第二个字符和子串T的开头进行匹配,但是你有没有发现,对于主串S来说,从第二个字符开始匹配的话,这种做法合理吗?是不是显得多余,因为匹配上的前三个字符abc各不相等,也就是说明了主串S的第二个字符和子串T的第一个字符a一定不相等,所以主串没有必要从第二个字符开始和T进行匹配,应该从第四个字符和T的首字符开始匹配,这就是KMP算法的核心思想,那么使用KMP算法后,匹配的示意图如下:

看到上面的示意图,可以看出KMP算法的思想,主串S直接跳过b和c,开始用d和子串T进行匹配,跳过了2个字节。

提高了一定的匹配效率。

上面是KMP算法处理的第一种情况,我们来看一下第二种情况,主串S为”abadef”,子串T为”abaf”,接下来我们看看匹配的过程:

由于主串S的前三个字符和子串T相等,那么现在我们就思考一下,这个时候,主串应该从第几个字符开始匹配,子串开始从第几个字符开始匹配呢?

我们观察到主串和子串开始匹配的串为“aba”,这个串有些特殊,就是这个串的前缀a和后缀a相等,那么对于这样的字符串,在没有匹配的情况下,子串T还是从第一个字符开始匹配吗?

当然不是,因为前缀和后缀相等,所以,子串T从前缀串的下一个字符开始和主串进行匹配即可,也就是从第二个字符b开始和主串的d开始匹配。

这样就少了一次移动,提高了一次效率,那么第二次匹配的过程如下:

相关推荐

ES6中 Promise的使用场景?(es6promise用法例子)

一、介绍Promise,译为承诺,是异步编程的一种解决方案,比传统的解决方案(回调函数)更加合理和更加强大在以往我们如果处理多层异步操作,我们往往会像下面那样编写我们的代码doSomething(f...

JavaScript 对 Promise 并发的处理方法

Promise对象代表一个未来的值,它有三种状态:pending待定,这是Promise的初始状态,它可能成功,也可能失败,前途未卜fulfilled已完成,这是一种成功的状态,此时可以获取...

Promise的九大方法(promise的实例方法)

1、promise.resolv静态方法Promise.resolve(value)可以认为是newPromise方法的语法糖,比如Promise.resolve(42)可以认为是以下代码的语...

360前端一面~面试题解析(360前端开发面试题)

1.组件库按需加载怎么做的,具体打包配了什么-按需加载实现:借助打包工具(如Webpack的require.context或ES模块动态导入),在使用组件时才引入对应的代码。例如在V...

前端面试-Promise 的 finally 怎么实现的?如何在工作中使用?

Promise的finally方法是一个非常有用的工具,它无论Promise是成功(fulfilled)还是失败(rejected)都会执行,且不改变Promise的最终结果。它的实现原...

最简单手写Promise,30行代码理解Promise核心原理和发布订阅模式

看了全网手写Promise的,大部分对于新手还是比较难理解的,其中几个比较难的点:状态还未改变时通过发布订阅模式去收集事件实例化的时候通过调用构造函数里传出来的方法去修改类里面的状态,这个叫Re...

前端分享-Promise可以中途取消啦(promise可以取消吗)

传统Promise就像一台需要手动组装的设备,每次使用都要重新接线。而Promise.withResolvers的出现,相当于给开发者发了一个智能遥控器,可以随时随地控制异步操作。它解决了三大...

手写 Promise(手写输入法 中文)

前言都2020年了,Promise大家肯定都在用了,但是估计很多人对其原理还是一知半解,今天就让我们一起实现一个符合PromiseA+规范的Promise。附PromiseA+规范地址...

什么是 Promise.allSettled()!新手老手都要会?

Promise.allSettled()方法返回一个在所有给定的promise都已经fulfilled或rejected后的promise,并带有一个对象数组,每个对象表示对应的pr...

前端面试-关于Promise解析与高频面试题示范

Promise是啥,直接上图:Promise就是处理异步函数的API,它可以包裹一个异步函数,在异步函数完成时抛出完成状态,让代码结束远古时无限回掉的窘境。配合async/await语法糖,可...

宇宙厂:为什么前端离不开 Promise.withResolvers() ?

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发。1.为什么需要Promise.with...

Promise 新增了一个超实用的 API!

在JavaScript的世界里,Promise一直是处理异步操作的神器。而现在,随着ES2025的发布,Promise又迎来了一个超实用的新成员——Promise.try()!这个新方法简...

一次搞懂 Promise 异步处理(promise 异步顺序执行)

PromisePromise就像这个词的表面意识一样,表示一种承诺、许诺,会在后面给出一个结果,成功或者失败。现在已经成为了主流的异步编程的操作方式,写进了标准里面。状态Promise有且仅有...

Promise 核心机制详解(promise机制的实现原理)

一、Promise的核心状态机Promise本质上是一个状态机,其行为由内部状态严格管控。每个Promise实例在创建时处于Pending(等待)状态,此时异步操作尚未完成。当异步操作成功...

javascript——Promise(js实现promise)

1.PromiseES6开始支持,Promise对象用于一个异步操作的最终完成(包括成功和失败)及结果值的表示。简单说就是处理异步请求的。之所以叫Promise,就是我承诺,如果成功则怎么处理,失败怎...

取消回复欢迎 发表评论: