算法五——字符串匹配(下)KMP

文章出处:极客时间《数据结构和算法之美》-作者:王争。该系列文章是本人的学习笔记。
KMP,是三个作者(D.E.Knuth,J.H.Morris和V.R.Pratt)的简称。
  KMP算法和BM一样,也是一个字符串匹配算法。其核心思想和BM也类似,查找规律,减少当发生字符不匹配的时候,回退的长度和次数,减少比较次数,降低时间复杂度。
  文章内容依然主要来自极客时间的算法专栏。同时参考了知乎文章。

1 字符串匹配是什么

字符串匹配就是想看看一个字符串在另一个字符串中的位置。例如查找字符串"ababacd"在字符串"ababaeabac"中出现的位置。这就是字符串查找。
在A中查找B,A称为主串,B称为模式串。
A:ababaeabac
B:ababacd
一般的查找方法是从第0位开始一一匹配字符。当找到第5位的时候发现字符不匹配。这时候从A的第1位开始,与B重头开始一一匹配。当匹配到B串末尾,则匹配成功。

	public static int indexOf(String a,String b){int idx = -1;int n = a.length();int m = b.length();int i=0;while(i<n){int j = 0;int pos = i;while(pos<n && j<m && a.charAt(pos)==b.charAt(j)){pos++;j++;}if(j==m){idx = i;break;}else{i++;}}return idx;}

2 这里介绍几个概念。

当查找匹配到不相同的字符的时候在主串中的那个字符成为坏字符
已经匹配的部分成为好前缀
字符串前缀子串:一个字符串S的前缀子串是指从下标0开始形成S的不同子串。例如S=ababa,S的前缀子串有:a,ab,aba,abab。
字符串后缀子串:一个字符串S的前缀子串是指以最后一位为结尾形成S的不同子串。例如S=ababa,S的后缀子串有:a,ba,aba,baba。
可匹配子串:是指长度相等的前后缀子串相等。例如上个例子中的子串’a’,‘aba’。就是可匹配子串
最长可匹配子串:在可匹配子串中找长度最长的子串。上个例子中是’aba’。
最长可匹配前缀子串最长可匹配后缀子串,虽然最长可匹配子串是相同的,但是前缀子串和后缀子串的起始位置和结束位置不同。例如上个例子中最长可匹配子串’aba’,作为最长可匹配前缀子串起止的位置是0和2,最长可匹配后缀子串的位置是2和4。为什么这么关心位置,在下面有介绍。
在减少移动次数的过程中,我们关心最长可匹配前缀子串C的终止位置x。下面描述中将用C表示最长可匹配前缀子串

前缀子串后缀子串是否匹配是否CC的终止位置
aa
abba
abaaba2
ababbaba

3 尽可能少移动

3.1目标是主串位置不动

是不是能够减少移动次数呢?可不可以保持主串中的位置不动,只移动模式串?例如下面这样。
ababaeabac
 ababacd
因为移动模式串,已经匹配的部分"ababa"就需要前缀子串与后缀子串能匹配得上,还要尽可能长,也就是上面概念中提到的最长可匹配前缀子串C。

从图中看到用模式串中"ababa"的前缀子串,去匹配主串中"ababa"的后缀子串。为了不会移动的过头这个匹配还需要使用最长的子串。例如下面这个就不可以。虽然"a"也是匹配子串,但是向右移动的更多了,可能会错过某些匹配。
 ababaeabac
   ababacd

3.2

这里发现字符串前缀子串和后缀子串匹配完全可以在模式串内进行,与主串无关。如果我们暴力枚举求字符串"ababa"的最长可匹配前缀子串C,算法复杂度必将很高,没有什么改进之处。
我们可以假设如果已经提前知道"ababa"的最长的那个可以和后缀匹配的前缀子串C=“aba”,并且知道C的终止位置是2(数组下标)。那模式串的下标就可以直接移动下标3了。

4 模式串求next数组

我们假设把"ababa"的最长可匹配前缀子串C的终止位置是2存在一个数组中,这个数组是next数组,称为失效函数。
我们假设在模式串“ababacd”的每一个位置都可能发生不匹配,则需要求模式串每个子串和本身的next数组值。
next数组下标表示模式串每个子串的结尾下标位置。值表示C字符串的终止位置。
next数组也被成为失效函数。
例如模式串:ababacd next数组值为下表。

发生不匹配时候已经匹配好的字符串结尾下标C的结尾下标
a0-1next[0]=-1
ab1-1next[1]=-1
aba20next[2]=0
abab31next[3]=1
ababa42next[4]=2
ababac5-1next[5]=-1

求next数组的值可以使用动态规划的思想。
假设next[i-1]=k。如果模式串S的k+1的字符与S的i是相同的,则next[i]=k+1。例如上个表格中next[2]=0,而且S.charAt(0+1)=S.charAt(2+1),所以next[3]=1。

如果模式串S的k+1的字符与S的i是不同的,说明字符串[0,i-1]的最长可匹配前缀子串C不可用。接着查找次长是不是满足条件。(为什么查找次长?因为想要利用之前已经计算的结果。否则就是从头开始计算了。也就没做什么优化了)

现在需要证明:S[0,i-1]的次长可匹配前缀子串 = S[0,k]的最长可匹配前缀子串

S[0, i-1] 的次长可匹配前缀子串X的前缀、后缀一定在S[0,k]范围内,而且是S[0,k]的前缀、后缀,那么S[0,k]的最长可匹配前缀子串=S[0,i-1]的次长可匹配前缀子串。
在这里插入图片描述

如果x>k,可以推出 S[0,x]长度>S[0,k]的长度,而且S[0,x]还是S[0,i-1]的可匹配前缀子串,那么S[0,x]应该是S[0,i-1]的最长可匹配前缀子串。这与S[0,k]是最长可匹配前缀子串矛盾。这证明了x<k。

进一步,因为x<k,要想找到S[0,i-1]的次长可匹配前缀子串,也就是要在S[0,k]找最长可匹配前缀子串。

例如对于对子字符串 abababzababab 来说,
前缀有 a, ab, aba, abab, ababa, ababab, abababz, …
后缀有 b, ab, bab, abab, babab, ababab, zababab, …
next[13]=6。“ababab” 是最长可匹配前缀子串,"abab"是次长可匹配前缀子串。

因为可匹配前缀子串位置从下标0开始的,所以对于同一个字符串的次长可匹配前缀子串一定是最长可匹配前缀子串的一部分,所以"abab" 是 “ababab” 的子串。

又因为"abab"是可匹配子串,所以"abab" 一定是 “ababab” 的前后缀,也就一定是“ababab” 的最长可匹配子串。假设"abab" 不是“ababab” 的最长可匹配子串,那对于“abababzababab”的次长子串一定是其他子串而不是"abab"。

所以“ababab”的最长可匹配前缀子串的终止位置=“abababzababab”次长可匹配前缀子串的终止位置。也就是代码k=next[k]。

继而,继续判断S[k+1]是不是和S[i]相同。

public class KMP {// b 表示模式串,m 表示模式串的长度private static int[] getNexts(char[] b, int m) {int[] next = new int[m];int k = -1;next[0] = -1;for(int i=1;i<m;i++){while(k!=-1 && b[k+1]!=b[i]){k = next[k];}//假设next[i-1]=k,如果模式串S的k+1的字符与S的i是相同的if(b[k+1] == b[i]){k++;}next[i] = k;}return next;}// a, b 分别是主串和模式串;n, m 分别是主串和模式串的长度。public static int kmp(char[] a, int n, char[] b, int m) {int[] next = getNexts(b,m);int j = 0;for(int i=0;i<n;i++){//当字符不匹配的时候,将模式串的指针回退到最长可匹配前缀字符的结束下标位置while(j>0 && a[i]!=b[j]){j= next[j-1]+1;}//字符匹配的时候if(a[i]==b[j]){j++;}if(j==m){return i-m+1;}}return -1;}public static void  main(String[] args){String a = "abbaabbaaba";String b = "a";int p = kmp(a.toCharArray(),a.length(),b.toCharArray(),b.length());System.out.println(p);}
}

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

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

相关文章

第四十九期:大牛总结的MySQL锁优化,写得太好了!

随着 IT 技术的飞速发展&#xff0c;各种技术层出不穷&#xff0c;让人眼花缭乱。尽管技术在不断更新换代&#xff0c;但是有些技术依旧被一代代 IT 人使用至今。 作者&#xff1a;崔皓 随着 IT 技术的飞速发展&#xff0c;各种技术层出不穷&#xff0c;让人眼花缭乱。尽管技…

数据结构八-Trie树

文章出处&#xff1a;极客时间《数据结构和算法之美》-作者&#xff1a;王争。该系列文章是本人的学习笔记。 1 Trie树的使用场景 搜索引擎中的搜索词建议。当你在搜索引擎中输入词&#xff0c;搜索引擎提示给你一个词的列表&#xff0c;帮助你快速输入想搜索的词。 这个功能…

[安全模型][Cambria Math][A][]敌手A-> 怎么打出来?

字母A 设置字体 “Cambria Math” -> &#x1d49c;

第五十期:详解语音识别技术的发展

在我们的生活中交流起着重要的作用。人类从符号开始&#xff0c;然后发展到用语言交流&#xff0c;再后来出现了计算和通信技术。在某些情况下&#xff0c;机器不仅与人类交流&#xff0c;也与机器之间交流。计算机和通信技术创造了互联网世界&#xff0c;正如我们所知的物联网…

打造移动测试云平台

转载来源&#xff1a;https://blog.csdn.net/toafu/article/details/80598977 背景 移动技术发展到现阶段&#xff0c;原生、混合式技术发展的足够成熟&#xff0c;可以无缝融合。而随着移动技术的发展和革新&#xff0c;移动领域的测试技术和实践也有了一定发展&#xff1a;工…

AC自动机:多模式串匹配实现敏感词过滤

文章出处&#xff1a;极客时间《数据结构和算法之美》-作者&#xff1a;王争。该系列文章是本人的学习笔记。 1 敏感词过滤场景 在很多支持用户发表内容的网站&#xff0c;都有敏感词过滤替换的功能。例如将一些淫秽、反动内容过滤掉&#xff0c;或者替换为****。在一些社交类…

[毕业论文][格式修改][摘要修改]毕业论文格式内容修改小技巧

背景 毕业季临近&#xff0c;盲审前对毕业论文进行最后一次逐行逐字修改&#xff0c;遇到的一些问题和解决办法 1.自动生成的目录行距可能不一。需要选中后单独再段落种设置行距 2.符号问题 配合查找发现问题 双引号 英文 “English” 中文“” 逗号 英文 English, 中文 &am…

第五十一期:AIOps落地关键点指南

随着越来越多企业愿意在运营中采用AIOps的模式&#xff0c;他们所要面对的问题是&#xff1a;如何以与业务需求相适应的方式来接受它。我们为您准备的一些有关AIOps落地关键点指南。 作者&#xff1a;陈峻 【51CTO.com快译】随着系统效率和复杂程度的日益提高&#xff0c;我们…

算法六——贪心

文章出处&#xff1a;极客时间《数据结构和算法之美》-作者&#xff1a;王争。该系列文章是本人的学习笔记。 1 背豆子的例子 假设我们有一个可以容纳 100kg 物品的背包&#xff0c;可以装各种物品。我们有以下 5 种豆子&#xff0c;每种豆子的总量和总价值都各不相同。为了让…

BZOJ 1827: [Usaco2010 Mar]gather 奶牛大集会 树形DP + 带权重心

Description Bessie正在计划一年一度的奶牛大集会&#xff0c;来自全国各地的奶牛将来参加这一次集会。当然&#xff0c;她会选择最方便的地点来举办这次集会。每个奶牛居住在 N(1<N<100,000) 个农场中的一个&#xff0c;这些农场由N-1条道路连接&#xff0c;并且从任意一…

第五十二期:Java开发数据库设计的14个技巧,你知道几个?

可以是一对一、一对多、多对多的关系。在一般情况下&#xff0c;它们是一对一的关系&#xff1a;即一张原始单据对应且只对应一个实体。在特殊情况下&#xff0c;它们可能是一对多或多对一的关系&#xff0c;即一张原始单证对应多个实体&#xff0c;或多张原始单证对应一个实体…

《鸟哥的Linux私房菜--基础篇》学习

第四章 显示日期与时间的指令&#xff1a;date 输入&#xff1a; (base) liyihuadeMacBook-Pro:~ liyihua$ date 输出&#xff1a; Thu Jun 6 08:44:02 CST 2019显示日历指令&#xff1a;cal 输入&#xff1a; (base) liyihuadeMacBook-Pro:~ liyihua$ cal 输出&#xff1a; J…

第五十三期:公司如何选择数据库?DynamoDB、Hadoop和MongoDB 大比拼

随着公司数据存储方式的不断扩展&#xff0c;本文旨在比较公司使用的一些更现代的数据库系统——了解DynamoDB&#xff0c;Hadoop和MongoDB可以提供哪些功能将帮助用户针对业务模型做出更好的决策。 作者&#xff1a;久谦 用户如何选择最能满足当前业务需求的数据库&#xff…

[引用格式][中文论文][毕业论文]毕业论文引用格式 英文引用文献间隔过大

英文引用文献间隔过大&#xff0c;需要选中该条英文引用文献&#xff0c;在段落设置的中文版式设置西文换行&#xff0c;设置后再进行微调 设置前 设置后 设置方法

动态规划——0-1背包问题

文章出处&#xff1a;极客时间《数据结构和算法之美》-作者&#xff1a;王争。该系列文章是本人的学习笔记。 1 0-1背包问题 背包能够承受的总重量一定w&#xff0c;每个物品的总量不同int[] weight表示。怎么放才能让背包中物品的总重量最大。 每次决定一种物品&#xff0c…

第五十四期:MongoDB与MySQL:如何选择

MongoDB和MySQL分别是领先的开源NoSQL和关系数据库。哪个最适合您的应用程序? 作者&#xff1a;XEyes行走的CODE来源 MongoDB和MySQL分别是领先的开源NoSQL和关系数据库。哪个最适合您的应用程序? 在1990年代的互联网泡沫时期&#xff0c;用于Web应用程序的一种通用软件堆栈…

动态规划——矩阵中的最短路径长度

文章出处&#xff1a;极客时间《数据结构和算法之美》-作者&#xff1a;王争。该系列文章是本人的学习笔记。 题目 假设我们有一个 n 乘以 n 的矩阵 w[n][n]。矩阵存储的都是正整数。棋子起始位置在左上角&#xff0c;终止位置在右下角。我们将棋子从左上角移动到右下角。每次…

[密码学基础][每个信息安全博士生应该知道的52件事][Bristol Cryptography][第19篇]Shamir密钥交换场景

这是一系列博客文章中最新的一篇&#xff0c;该文章列举了“每个博士生在做密码学时应该知道的52件事”:一系列问题的汇编是为了让博士生们在第一年结束时知道些什么。 Shamir密钥交换场景是一个被Adi Shamir提出的算法.算法允许多方分割一个密码,例如一个密钥.当足够多的秘密结…

第五十五期:MongoDB数据库误删后的恢复

如果部署的是 MongoDB 复制集&#xff0c;这时还有一线希望&#xff0c;可以通过 oplog 来尽可能的恢复数据;MongoDB 复制集的每一条修改操作都会记录一条 oplog&#xff1b;如果对 MongoDB 做了全量备份 增量备份&#xff0c;那么可以通过备份集及来恢复数据。 作者&#xf…

037-PHP如何返回闭包函数实例

<?php /*: 如何返回闭包函数实例*/# 直接调用将不会输出$txt的内容function demo(){$txt 我爱PHP;$func function () use ($txt) {echo $txt;};# 这里不再直接调用&#xff0c;而且是把实例返回return $func; # 区别于直接写 $func;}# 测试一下 $res demo(); // 函数返…