用JS写KPM算法

码农不哭
• 阅读 2773

最近公司启动小程序项目中,在搜索模块有这么个功能需求:当用户输入搜索内容时实时地请求服务器得到一组较高匹配度的搜索关键字,在这些关键字中高亮显示用户的匹配输入。

例如输入“中国”
搜索关键字为“中国共产党”

那我就想到了KPM算法,就打开《大话数据结构》这本书来看看KPM到底是什么东西,倒腾了很久,终于对KPM算法有一点点点点点点了解,就来记录一下。


传统的字符串匹配算法

传统的字符串匹配算法是这样子的:当目标字符串和匹配字符串在匹配过程中发生失配,目标字符串下标和匹配字符串下标都要回溯,这会导致一些不必要的匹配判断,举个栗子。

用JS写KPM算法

当匹配到第4位的时候,偶哦匹配失败,那么将会从下面的位置开始匹配

用JS写KPM算法

但是我们明眼看去,很明显是多余匹配判断了吗。
对没错,传统的匹配算法会只是很简单的匹配回溯匹配回溯,时间复杂度为O((n-m)*m),导致很多的匹配判断是多余的,那么接下来的KPM算法就是解决这个笨重的问题的。

KPM算法

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。时间复杂度O(m+n)。
注:一下i代表目标字符串的下标,j代表匹配字符串的下标。
刚才讲到,我们的KPM算法就是为了让这没必要的回溯发生,那么i不可变小,就只考虑变化j值了。通过观察发现,如果匹配字符串中有相同的子字符串,那么j的变化会有所不同。所以这个j值的变化跟目标字符串没什么关系,只跟自己的子字符串的重复性有关。

next[j] = -1 // 当j == 0时
        = max{k|0<k<j 且p[0]... p[k] == p[j-k]...p[j-1]}
        = 0 // 其他情况

例如:

j:       0 1 2 3 4
P:       A B A B C
next[j]:-1 0 0 1 2   

说白了就是j前的子字符串的重复字符有多少个,那么next[j]就是重复字符串个数。
next数组的作用就是KPM算法j值的回溯方案。
还是上面那么例子。当i = 4, j = 4时C !== X ,那么这个时候j = next[j] = 2,所以只需进行target[i(为4)]和pattern[j(为2)]的判断,而pattern[0]和pattern[1]分别的判断省略掉了。

说了那么多太干了,贴个代码先。

var target = 'ababxababc'
var pattern = 'ababc'

function getKPMNext(str) {
    var i, j;
    var next = [];

    next[0] = -1;
    i = 0; j = -1;

    while(i < str.length - 1) {
        if (j == -1 || str[i] == str[j]) {
            next[++i] = ++j;
        } else {
            j = next[j];
        }
    }
    return next;
}

// j = next[j] next[j]的两侧子字符串相等,所以这时候str[i] == str[j] 倒数两位== 4 5位 == 1 2位
console.log(getKPMNext('abxabaabxabxa'))

代码量不多但理解起来有点困难(反正我理解了很久T_T)。j = -1的时候就是上述next数组在其他情况。
当str[i] == str[j]的时候,那么i和j就很愉快地手牵手地前进。
当str[i] != str[j]的时候,那么j就要回溯。

然后就是KPM算法的主体了

function KPMMatch(target, pattern) {
    var i = 0, j = 0;
    var next = getKPMNext(pattern);

    while (i < target.length && j < pattern.length) {
        if (j == -1 || target[i] == pattern[j]) {
            ++i;
            ++j;
        } else {
            j = next[j];
        }
    }

    if (j >= pattern.length) {
        return i - j;
    }
    return -1;
}

当失配时j回溯,相对于传统匹配省掉了不必要的匹配。

点赞
收藏
评论区
推荐文章
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
待兔 待兔
1年前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Karen110 Karen110
4年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
昔不亏 昔不亏
4年前
「小程序 — 云开发」搜索跳转
样式如图:在home.wxml中js<confirmtype:键盘的右下角按钮显示'搜素'bindconfirm:按下键盘'搜索'按钮bindinput:在输入框输入过程中触发事件bindtap:点击搜索图标<viewclass"search"<inputtype"text"placeholder"搜索菜品"con
Stella981 Stella981
4年前
Lucene系列六:Lucene搜索详解(Lucene搜索流程详解、搜索核心API详解、基本查询详解、QueryParser详解)
一、搜索流程详解1\.先看一下Lucene的架构图!(https://oscimg.oschina.net/oscnet/f99b42f5233e8afba2477e1f5ba2e087f9f.png) 由图可知搜索的过程如下:  用户输入搜索的关键字、对关键字进行分词、根据分词结果去索引库里面找到对应的文章id、根据
Wesley13 Wesley13
4年前
ES[7.6.x]学习笔记(十二)高亮 和 搜索建议
ES当中大部分的内容都已经学习完了,今天呢算是对前面内容的查漏补缺,把ES中非常实用的功能整理一下,在以后的项目开发中,这些功能肯定是对你的项目加分的,我们来看看吧。高亮高亮在搜索功能中是十分重要的,我们希望搜索的内容在搜索结果中重点突出,让用户聚焦在搜索的内容上。我们看看在ES当中是怎么实现高亮的,我们还用之前的索引ik_index,前面
Stella981 Stella981
4年前
ElasticSearch + Canal 开发千万级的实时搜索系统
公司是做社交相关产品的,社交类产品对搜索功能需求要求就比较高,需要根据用户城市、用户ID昵称等进行搜索。项目原先的搜索接口采用SQL查询的方式实现,数据库表采用了按城市分表的方式。但随着业务的发展,搜索接口调用频次越来越高,搜索接口压力越来越大,搜索数据库经常崩溃,从而导致搜索功能经常不能使用。!(https://oscimg.oschina.n
Stella981 Stella981
4年前
Mac系统下Android生成keystore
1.首先打开终端(在搜索里面搜索Te即可出来)2.然后输入 cd/Library/Java/Home/bin/3.然后这步很关键,由于我们用的是当前用户,所以没有最高权限,不能在Library文件夹下生成任何文件,所以照抄网上的方法是无法创建成功的,复制粘贴步骤4的内容。4.keytoolgen
Wesley13 Wesley13
4年前
MySQL面试(二)
1、为什么索引遵循最左匹配原则?  当B树的数据项是符合的数据结构,比如(name,age,sex)的时候,B树是按照从左到右的顺序建立搜索树的。比如当(张三,20,F)这样的数据来检索的时候,b树会优先比较name来确定下一步的所搜方向,如果name相同再依次比较age和sex,最后得到检索的数据;但当(20,F)这样的没有name的数据来的时候
Stella981 Stella981
4年前
Flink实时构建倒排索引与全文检索
!(https://oscimg.oschina.net/oscnet/077ed19b13d84bbcbe4b0244c8d0f50f.jpg)对于搜索引擎,大家不会感到陌生,我们每天都在用。我们在百度、谷歌上搜索我们想要的信息。比如,在输入框里输入关键字查询后,会返回很多和关键字相关的内容。或者在电商网站输入想
绣鸾 绣鸾
2年前
PDF Search for Mac(pdf文件搜索工具)
是一款Mac平台上的PDF文件搜索工具,可以帮助用户快速地搜索和查找Mac电脑上的PDF文件。该软件提供了快速、准确、可靠的搜索和查找功能,可以让用户轻松地搜索和查找PDF文档中的关键字、短语和其他内容。此外,PDFSearch还提供了一些实用的特性,例如
码农不哭
码农不哭
Lv1
每个城市都会下雨,就像我走到那里都会想你。
文章
3
粉丝
0
获赞
0