算法五——字符串匹配(中)

文章出处:极客时间《数据结构和算法之美》-作者:王争。该系列文章是本人的学习笔记。

1文本编辑器中的查找功能怎么实现

在word中有一个功能:查找某个字符串,将其替换为另一个字符串,就会用到这个功能。
需要新算法的原因是:word可能特别长,BF算法可能性能退化很严重;RK算法需要设计一个能够处理所有字符串的哈希函数,这并不容易。
  新的算法BM,根据实验统计,其性能是KMP的3~4倍。

2算法的核心思想

我们把模式串和主串匹配的过程看做是模式串在主串中不断向后匹配的过程。当遇到不匹配的字符的时候BF和RK都是向后移动一位。那可不可以多移动几位呢?
  
  在例子中主串中的c在模式串中不存在,可以直接将模式串移动到c后面。
  
  由现象找规律。当遇到不匹配的字符的时候,有什么规律可以让模式串一次移动好几位呢?
  BM算法就是在找这种规律。规律的核心分为坏字符和好后缀两个规则。

2.1 坏字符规则

我们从模式串的末尾往前倒着匹配,当我们发现某个字符没法匹配的时候,把这个没有匹配的字符叫做坏字符(主串中的字符串)。
  当发生不匹配的时候,我们把坏字符在模式串中出现的位置记为si,如果坏字符在模式串中存在,则找到在模式串中最右边的位置记为xi,不存在xi=-1。模式串向后移动的位数:si-xi。
  
  我们用数组bc[i]表示字符i在模式串中出现的位置。当模式串中有多个字符i的时候,bc[i]保存的是i的最大位置。这是为了防止过度偏移。
  在上图中,当再次发生不匹配的时候bc[a]=0。这时候偏移2-0=2。
  坏字符规则代码部分。

	// 数据包含的字符范围private static final int SIZE = 256;private void generateBC(char[] b, int m, int[] bc) {Arrays.fill(bc, -1);for (int i = 0; i < m; i++) {bc[(int) b[i]] = i;}}/*** 使用BM算法,查找字符串b在字符串a中出现的首位置。如果没有出现,返回-1.* * @param a* @param b* @return*/public int bm(char[] a, char[] b) {int n = a.length, m = b.length;int[] bc = new int[SIZE];generateBC(b, m, bc);int i = 0;while (i <= n - m) {int j = m - 1;while (j >= 0 && a[i + j] == b[j]) {j--;}if (j == -1)return i;int x = j - bc[(int) a[i + j]];		i = i + x;}return -1;}

特殊情况下比如主串是 aaaaaaaaaaaaaaaa,模式串是 baaaa。模式串不但不会向后移动,还会向前走。这是因为,假设i=0,j从m-1开始匹配,匹配到j=0的时候发生不匹配,坏字符为a,suffix[a]=4,j-suffix[‘a’]=-4,i=i-4。i的值 不断减小。
  这个时候就需要第二个规则了。

2.2 好后缀规则

模式串和主串已经匹配好的部分,叫做好后缀。记为{u}。
  模式串中可能只有一个好后缀,也有可能包含至少2个好后缀。分两种情况讨论。

2.2.1 模式串中至少包含2个u


  我们查找{u},在模式串中是否还有出现。从右向左查找第一个出现{u}的匹配串记为{u*}。将{u*}滑动到主串与{u}匹配的位置。也就是不同于好后缀,但是是最后一次出现u的起始位置。

2.2.2 模式串中只有一个u

如果不能在模式串中找到另外一个u,怎么办?直接滑动到主串{u}的后面,会有过渡滑动的可能性。
  介绍几个概念。
  字符串s的后缀子串是指 与最后一个字符跟 字符串s 对齐的子串,比如 abc 的后缀子串包括 c, bc。前缀子串是指起始字符与字符串s对齐的子串。abc的前缀子串包括a,ab。
  如果好后缀在模式串中不存在可匹配的子串,那在我们一步一步往后滑动模式串的过程中,只要主串中的{u}与模式串有重合,那肯定就无法完全匹配。但是当模式串滑动到前缀与主串中{u}的后缀有部分重合的时候,就有可能会存在完全匹配的情况。
  所以,针对这种情况,需要考察好后缀的后缀子串,是否存在跟模式串的前缀子串匹配的。
  
 例如图中第一次发生不匹配的时候,模式串位置j=4,对应主串位置x=7,好后缀是字符串bc。bc在模式串中只出现了一次。如果我们整体向右移动模式串长度 m(=7),那就可能错过一次匹配。因为在这里好后缀bc,有匹配的前缀子串c。将前缀字符串c和好后缀子串c对应起来,再次匹配即可。

2.2.3 好后缀规则编码

首先我们总结好后缀规则。令{u}=好后缀。
如果模式串中包含另外一个子字符串{u*}={u},则找到这个这个{u*}的起始位置,移动。

如果模式串中不包含另外一个子字符串{u*},则找到{u}的最长前缀匹配字符串{v}的起始位置,移动。

先考虑怎么表示一个字符串的后缀。对于所有后缀字符串的结束字符都是一样的。我们可以用不同的长度表示后缀字符串 。用数组suffix[i]表示长度为i的后缀字符串 ,在模式串中出现的起始位置。

用prefix[i]表示长度为i的后缀字符串是否有匹配的前缀子串。

 
 怎么得到这两个数组有编码上的技巧。具体看代码。

2.3 怎么选择坏字符和好后缀规则

我们可以分别计算好后缀和坏字符往后滑动的位数,然后取两个数中最大的,作为模式串往后滑动的位数。

3 BM代码实现

public class BM {// 数据包含的字符范围private static final int SIZE = 256;private void generateBC(char[] b, int m, int[] bc) {Arrays.fill(bc, -1);for (int i = 0; i < m; i++) {bc[(int) b[i]] = i;}}/*** 使用BM算法,查找字符串b在字符串a中出现的首位置。如果没有出现,返回-1.* * @param a* @param b* @return*/public int bm(char[] a, char[] b) {int n = a.length, m = b.length;int[] bc = new int[SIZE];int[] suffix = new int[m];boolean[] prefix = new boolean[m];generateBC(b, m, bc);generatorGS(b, m, suffix, prefix);int i = 0;while (i <= n - m) {int j = m - 1;while (j >= 0 && a[i + j] == b[j]) {j--;}if (j == -1)return i;int x = j - bc[(int) a[i + j]];int y = 0;if(j < m - 1) {y = moveByGS(b, m, j, suffix, prefix);}		i = i + Math.max(x, y);}return -1;}/*** 生成后缀子串完整匹配的位置 后缀子串可匹配的最长前缀子串* * @param b* @param m* @param suffix* @param prefix*/private void generatorGS(char[] b, int m, int[] suffix, boolean[] prefix) {Arrays.fill(suffix, -1);for (int i = 0; i < m - 1; i++) {int k = 0;int j = i;while (j >= 0 && b[j] == b[m - 1 - k]) {j--;k++;suffix[k] = j + 1;}if (j == -1) {prefix[k] = true;}}}/*** 按照好后缀规则,当j位置发生比匹配的时候应该移动几步* * @param b* @param j* @param suffix* @param prefix* @return*/private int moveByGS(char[] b, int m, int j, int[] suffix, boolean[] prefix) {int k = m - 1 - j;//好后缀长度if(suffix[k] != -1) return j - suffix[k] + 1;for(int r = j+2; r < m; r++) {if(prefix[m - r] == true) {return r;}}return 0;}public static void main(String[] args) {String a = "abcacabdc";String b = "abd";int postion = new BM().bm(a.toCharArray(), b.toCharArray());System.out.println(postion);}}

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

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

相关文章

第四十七期:漫画:什么是公有云、私有云和混合云?

为了方便大家理解&#xff0c;我们尽量用通俗的语言和举例子的方式讲解&#xff0c;并且文中还配备了漫画供大家参考学习。 作者&#xff1a;漫话编程 为了方便大家理解&#xff0c;我们尽量用通俗的语言和举例子的方式讲解&#xff0c;并且文中还配备了漫画供大家参考学习。 …

第四十八期:你太菜了,竟然不知道Code Review?

我一直认为Code Review(代码审查)是软件开发中的优秀实践之一&#xff0c;可以有效提高整体代码质量&#xff0c;及时发现代码中可能存在的问题。 作者&#xff1a;宝玉 我一直认为Code Review(代码审查)是软件开发中的优秀实践之一&#xff0c;可以有效提高整体代码质量&…

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

文章出处&#xff1a;极客时间《数据结构和算法之美》-作者&#xff1a;王争。该系列文章是本人的学习笔记。 KMP&#xff0c;是三个作者&#xff08;D.E.Knuth&#xff0c;J.H.Morris和V.R.Pratt&#xff09;的简称。   KMP算法和BM一样&#xff0c;也是一个字符串匹配算法。…

第四十九期:大牛总结的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;终止位置在右下角。我们将棋子从左上角移动到右下角。每次…