模式匹配算法----KMP算法以及next数组的解法

KMP算法:求字符串匹配(也叫模式匹配)的算法,即给定一个字符串,求其某一子串在其中出现的位置。

普通模式匹配

例如:给定字符串为abcabaaabaabcac,求其子串abaabcac在其中出现的位置。

结果为7

对于这种问题,没有经验的编程者通常会采用逐个匹配的方法,来得出结果。这就是最简单一种算法思想。

1. 逐个进行比较,如果相同,就继续比较下一个,但是我们可以看到下图中,c与a不相同,这就是所谓的“失配”。

2. 当发生失配,我们会将子字符串逐个后移,直到新的匹配建立,再逐个比较

3.

4.

5.

6.

7.

根据上面的方法,我们可以看到第二步有两个图,这是为什么呢?这是因为当出现失配时候,字符串每次后移一个的方法,不能够立刻建立匹配,还需要继续后移才能建立。也就是说,这种情况下,后移两个才能建立匹配。

当然这个例子比较小,只出现了两次这种情况,但是如果是较大的数据,这种冗余操作是十分低效的。这也就是KMP算法优化的地方。

KMP算法--next[]

KMP算法会创建一个next[]数组,用来保存一个字符失配后,到底跳转到第几个的位置才能更快速的建立匹配。这里用了跳转这个词,因为并不是向后移动next[i]位,而应该是直接跳转到相应的位置,使失配位与第next[i]位相对。这个数组是KMP算法的核心。

下面以字符串abaabcac为例,求解next数组,模式匹配的数组下标从1开始(这个算法推荐这么做,很多事情没有那么多理由)

 

next数组按照最长相等前后缀长度求解:

next[1],字符串“a”,前缀{},后缀{},没有相等前后缀,next记为0。要注意,前后缀均不包括整个串。

next[2],字符串“ab”,前缀{a},后缀{b},没有相等前后缀,next记为0。

next[3],字符串“aba”,前缀{a,ab},后缀{a,ba},相等前后缀{a},长度为1,next记为1。

next[4],字符串“abaa”,前缀{a,ab,aba},后缀{a,aa,baa},相等前后缀{a},长度为1,next记为1。

next[5],字符串“abaab”,前缀{a,ab,aba,abaa},后缀{b,ab,aab,baab},相等前后缀{ab},长度为2,next记为2。

next[6],字符串“abaabc”,前缀{a,ab,aba,abaa,abaab},后缀{c,bc,abc,aabc,baabc},相等前后缀{},next记为0。

next[7],字符串“abaabca”,前缀{a,ab,aba,abaa,abaab,abaabc},后缀{a,ca,bca,abca,aabca,baabca},相等前后缀{a},长度为1,next记为1。

next[8],字符串“abaabcac”,前缀{a,ab,aba,abaa,abaab,abaabc,abaabca},后缀{c,ac,cac,bcac,abcac,aabcac,baabcac},相等前后缀{},next记为0。

i12345678
str.charAt(i)abaabcac
next[i]00112010

由于使用next[]时,每次失配,都需要找它前面一个元素的next[]进行移动,为了方便,我们将next数组右移一位。最左侧填充-1,舍弃最右侧的元素。

i12345678
str.charAt(i)abaabcac
next[i]-10011201

为了简化计算,我们对next[]整体+1,这里得到的是我们通常使用的next[]。

i12345678
str.charAt(i)abaabcac
next[i]01122312

 

求解next数组代码:

    private static int[] get_Next(String str){ int[] next = new int[str.length()+1];  int j = next[2];// 这里next[1]和next[2]直接赋值next[1] = 0;next[2] = 1;for(int i = 2; i < str.length(); i++) {  while(j > 0 && str.charAt(i-1) != str.charAt(j)) {j = next[j]; }if(str.charAt(i-1) == str.charAt(j)) {j++;}next[i] = j;}  return next; }  

KMP算法改进--nextval[]

比较当前next[i]的值,与

1.前两位必为0,1

计算nextval[3]时,我们可以知道第三位字符位‘a’,next[3]=1,故查看第1位的字符为‘a’,字符相同,所以nextval[3]=next[1]=0

计算nextval[4]时,我们可以知道第四位字符位‘a’,next[4]=2,故查看第2位的字符为‘b’,字符不同,所以nextval[4]=next[4]=2

计算nextval[5]时,我们可以知道第五位字符位‘b’,next[5]=2,故查看第2位的字符为‘b’,字符相同,我们知道next[2]=1,故继续查看第1位的字符为‘a’,字符不同,所以nextval[5]=next[2]=1

计算nextval[6]时,我们可以知道第六位字符位‘c’,next[6]=3,故查看第3位的字符为‘a’,字符不同,所以nextval[6]=next[6]=3

计算nextval[7]时,我们可以知道第七位字符位‘a’,next[7]=1,故查看第1位的字符为‘a’,字符相同,所以nextval[7]=next[1]=0

计算nextval[8]时,我们可以知道第八位字符位‘c’,next[8]=2,故查看第2位的字符为‘b’,字符不同,所以nextval[7]=next[7]=2

i12345678
str.charAt(i)abaabcac
nextval[i]01021302

根据next数组,我们再来做一遍这个题目:给定字符串为abcabaaabaabcac,求其子串abaabcac在其中出现的位置。

 

1.创建匹配,第4个字符发生失配

2.使失配位与子串的第next[4]=2位相对,但是这是第二个字符又失配了

3.使失配位与子串的第next[2]=1位相对,此时第一个字符就失配

4.使失配位与子串的第next[1]=0位相对,注意我们的next数组是从1开始算的,所以相当于整体后移一个单位,下图的失配位实际是"c"

5.使失配位与子串的第next[4]=2位相对

6.使失配位与子串的第next[4]=2位相对

7.匹配成功

完整代码如下:

public class Main {  public static void main(String[] args) {  String str = "abaabcac";  String orig ="aabaabcac";  int[] next = get_Next(str);  for (int i = 1; i < next.length; i++) {System.out.print(next[i] + " ");}System.out.println();search(orig, str, next);  }  //next[i]表示的是str的"部分匹配表",这个表表示的是str前缀与后缀的最长公共字符串的长度private static int[] get_Next(String str){ int[] next = new int[str.length()+1];  int j = next[2];// 这里next[1]和next[2]直接赋值next[1] = 0;next[2] = 1;// 第2位之后的next,为什么从2开始,不是已经赋值了吗?这在于str.charAt(0)为字符串的第一个字母for(int i = 2; i < str.length(); i++) {  while(j > 0 && str.charAt(i-1) != str.charAt(j)) {j = next[j]; }if(str.charAt(i-1) == str.charAt(j)) {j++;}next[i] = j;}  return next; }  //orig为主串,而find为模式串,查找匹配位置以及匹配长度  private static void search(String orig, String find, int[]next){  int j = next[0];  for(int i = 0;i < orig.length(); i++){  while(j > 0 && orig.charAt(i) != find.charAt(j))  j = next[j];  if(orig.charAt(i) == find.charAt(j)){  j++;  }if(j == find.length()){  System.out.println("find at position " + (i - j+1));    System.out.println(orig.subSequence(i - j + 1, i + 1));    j = next[j];}}}
}

当然还有一种BM算法效率比KMP算法还好。有兴趣的读者可以参考时空权衡在模式匹配算法中的应用(JAVA)--Horspool算法(简化版BM算法)

nextval数组实际的数值是明显要小于next数组的,nextval数组由于考虑到了移动到的位置的数与当前位置数的关系,可以减少移动的距离。

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/569710.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot使用layui的字体图标时无法正常显示 解决办法

在html文件使用字体图标并且预览时正常&#xff0c;但是启动工程后显示不正常&#xff0c;浏览器调试界面显示字体文件无法decode&#xff1a; Failed to decode downloaded font: xxxxx 如图所示&#xff1a; 显示结果&#xff1a; 原因&#xff1a;经过maven的filter&#xf…

蓝桥杯第七届国赛JAVA真题----机器人塔

机器人塔X星球的机器人表演拉拉队有两种服装&#xff0c;A和B。 他们这次表演的是搭机器人塔。 类似&#xff1a;队内的组塔规则是&#xff1a; A 只能站在 AA 或 BB 的肩上。B 只能站在 AB 或 BA 的肩上。你的任务是帮助拉拉队计算一下&#xff0c;在给定A与B的人数时&…

Python divmod() 函数

Python divmod() 函数 Python 内置函数 python divmod() 函数把除数和余数运算结果结合起来&#xff0c;返回一个包含商和余数的元组(a // b, a % b)。 在 python 2.3 版本之前不允许处理复数。 函数语法 divmod(a, b)参数说明&#xff1a; a: 数字b: 数字实例 >>>div…

蓝桥杯第六届省赛JAVA真题----垒骰子

垒骰子 赌圣atm晚年迷恋上了垒骰子&#xff0c;就是把骰子一个垒在另一个上边&#xff0c;不能歪歪扭扭&#xff0c;要垒成方柱体。 经过长期观察&#xff0c;atm 发现了稳定骰子的奥秘&#xff1a;有些数字的面贴着会互相排斥&#xff01; 我们先来规范一下骰子&#xff1a;1 …

并发队列、线程池、锁

1、CountDownLatch(计数器) CountDownLatch 类位于java.util.concurrent包下&#xff0c;利用它可以实现类似计数器的功能。比如有一个任务A&#xff0c;它要等待其他任务执行完毕之后才能执行&#xff0c;此时就可以利用CountDownLatch来实现这种功能了。CountDownLatch是…

蓝桥杯第七届省赛JAVA真题----剪邮票

剪邮票 如【图1.jpg】, 有12张连在一起的12生肖的邮票。 现在你要从中剪下5张来&#xff0c;要求必须是连着的。 &#xff08;仅仅连接一个角不算相连&#xff09; 比如&#xff0c;【图2.jpg】&#xff0c;【图3.jpg】中&#xff0c;粉红色所示部分就是合格的剪取。 请你…

目标检测之YOLO V2 V3

YOLO V2 YOLO V2是在YOLO的基础上&#xff0c;融合了其他一些网络结构的特性&#xff08;比如&#xff1a;Faster R-CNN的Anchor,GooLeNet的\(1\times1\)卷积核等&#xff09;&#xff0c;进行的升级。其目的是弥补YOLO的两个缺陷&#xff1a; YOLO中的大量的定位错误和基于区域…

蓝桥杯第八届省赛JAVA真题----迷宫

标题&#xff1a;迷宫 X星球的一处迷宫游乐场建在某个小山坡上。 它是由10x10相互连通的小房间组成的。 房间的地板上写着一个很大的字母。 我们假设玩家是面朝上坡的方向站立&#xff0c;则&#xff1a; L表示走到左边的房间&#xff0c; R表示走到右边的房间&#xff0…

掌握MySQL数据库这些优化技巧,事半功倍!

一个成熟的数据库架构并不是一开始设计就具备高可用、高伸缩等特性的&#xff0c;它是随着用户量的增加&#xff0c;基础架构才逐渐完善。这篇文章主要谈谈MySQL数据库在发展周期中所面临的问题及优化方案&#xff0c;暂且抛开前端应用不说&#xff0c;大致分为以下五个阶段&am…

蓝桥杯第八届省赛JAVA真题----方格分割

标题&#xff1a;方格分割 6x6的方格&#xff0c;沿着格子的边线剪开成两部分。 要求这两部分的形状完全相同。 如图&#xff1a;p1.png, p2.png, p3.png 就是可行的分割法。 试计算&#xff1a; 包括这3种分法在内&#xff0c;一共有多少种不同的分割方法。 注意&#xff…

Python猫荐书系列:文也深度学习,理也深度学习

最近出了两件大新闻&#xff0c;相信大家可能有所耳闻。 我来当个播报员&#xff0c;给大家转述一下&#xff1a; 1、中国队在第 11 界罗马尼亚数学大师赛&#xff08;RMM&#xff09;中无缘金牌。该项赛事是三大国际赛事之一&#xff0c;被誉为中学奥数的最高难度。其中一道题…

NCRE四级网络工程师考题详解----LRU与LFU的区别

最近最少使用页面置换算法&#xff08;LRU&#xff09;淘汰的是最长时间不使用的 最近最不常用页面置换算法&#xff08;LFU&#xff09;淘汰的是一定时间内未被使用的 我们假设有主存块为3&#xff0c;所需页面的走向为2 1 2 1 2 3 4 注意,当调页面4时会发生缺页中断 若按L…

移动端与PC端页面布局区别

视口 视口是移动设备上用来显示网页的区域&#xff0c;一般会比移动设备可视区域大&#xff0c;宽度可能是980px或者1024px&#xff0c;目的是为了显示下整个为PC端设计的网页&#xff0c;这样带来的后果是移动端会出现横向滚动条&#xff0c;为了避免这种情况&#xff0c;移动…

蓝桥杯历届试题----斐波那契(矩阵快速幂)

问题描述 斐波那契数列大家都非常熟悉。它的定义是&#xff1a; f(x) 1 …. (x1,2) f(x) f(x-1) f(x-2) …. (x>2) 对于给定的整数 n 和 m&#xff0c;我们希望求出&#xff1a; f(1) f(2) … f(n) 的值。但这个值可能非常大&#xff0c;所以我们把它对 f(m) 取模…

蓝桥杯第七届国赛JAVA真题----七星填数

七星填数 如图【图1.png】所示。 在七角星的14个节点上填入1~14 的数字&#xff0c;不重复&#xff0c;不遗漏。 要求每条直线上的四个数字之和必须相等。 图中已经给出了3个数字。 请计算其它位置要填充的数字&#xff0c;答案唯一。 填好后&#xff0c;请提交绿色节点的4个…

洛谷 P1219 ---- 八皇后

题目描述 检查一个如下的6 x 6的跳棋棋盘&#xff0c;有六个棋子被放置在棋盘上&#xff0c;使得每行、每列有且只有一个&#xff0c;每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。 上面的布局可以用序列2 4 6 1 3 5来描述&#xff0c;第i个数字表示在第i行…

PXE(preboot execution environment):【网络】预启动执行环节:引导 live光盘 ubuntu livecd 16.4:成功...

default menu.c32label ubuntu menu label ubuntu (version 16.04) kernel ub1604/casper/vmlinuz append root/dev/nfs bootcasper netbootnfs nfsroot192.168.56.1:/e/_temp/ub1604 initrdub1604/casper/initrd quiet splash --# 关键是搭建nfs相应的服务器器并export解压后的…

Mysql 外键创建失败原因

最近在学习PHP&#xff0c;在用mysql创建表的时候总是创建不出来&#xff0c;我用的是Navicat做的&#xff0c;虽然建不出来外键&#xff0c;但是会创建出来一个索引&#xff0c;后来才明白&#xff0c;一定要有了对应的索引才能创建外键。

PHP实现简单注册登录系统

目录结构如下&#xff0c;其中function文件夹下包含两个函数文件&#xff0c;uploads文件夹用于存放上传的文件。 注&#xff1a;博主使用的是php5&#xff0c;使用php7的小伙伴运行报错的话有一部分原因是新的语法造成的&#xff0c;修改成新语法就可以了 html页面 登录页面…

蓝桥杯第九届省赛JAVA真题----螺旋折线

标题&#xff1a;螺旋折线 如图p1.pgn所示的螺旋折线经过平面上所有整点恰好一次。 对于整点(X, Y)&#xff0c;我们定义它到原点的距离dis(X, Y)是从原点到(X, Y)的螺旋折线段的长度。 例如dis(0, 1)3, dis(-2, -1)9 给出整点坐标(X, Y)&#xff0c;你能计算出dis(X, Y)…