算法五——字符串匹配(中)
文章出处:极客时间《数据结构和算法之美》-作者:王争。该系列文章是本人的学习笔记。
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);}}
算法五——字符串匹配(中)相关推荐
- 算法五——字符串匹配(上)
文章内容.图片均来自极客时间. 如何借助哈希算法实现高效字符串匹配 1 概念和用途 字符串匹配:查找一个字符串A在字符串B中是否出现,这个过程就是字符串匹配.A称为模式串,B称为主串.主串的长度记为n ...
- 【字符串系列】字符串匹配中的位并行算法
[字符串系列]字符串匹配中的位并行算法 最近一段时间看了一点"柔性字符串匹配", 发现位并行算法在字符串匹配这个领域还是很有用的, 下面抒发一下鄙见. 首先, 字符串位并行算法在a ...
- java 蓝桥杯算法提高 字符串匹配(题解)
试题 算法提高 字符串匹配 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述 给出一个字符串和多行文字,在这些文字中找到字符串出现的那些行.你的程序还需支持大小写敏感选项:当选项打开时 ...
- 【C#】KPM算法解决字符串匹配问题
KPM算法解决字符串匹配问题 什么是KPM算法 步骤 Ⅰ根据<最大长度表>部分匹配表(next) 寻找最长前缀后缀 Ⅱ 根据 部分匹配表 进行匹配 代码实现 什么是KPM算法 Knut ...
- 【数据结构与算法】字符串匹配 AC自动机
单模式串匹配 BF 算法和 RK 算法 BM 算法和 KMP 算法 多模式串匹配算法 Trie 树和 AC 自动机 AC 自动机 AC 自动机实际上就是在 Trie 树之上,加了类似 KMP 的 ne ...
- 【数据结构与算法】字符串匹配 KMP 算法
单模式串匹配 BF 算法和 RK 算法 BM 算法和 KMP 算法 多模式串匹配算法 Trie 树和 AC 自动机 KMP 算法 KMP 算法是根据三位作者(D.E.Knuth,J.H.Morris ...
- 【数据结构与算法】字符串匹配 BM算法
单模式串匹配 BF 算法和 RK 算法 BM 算法和 KMP 算法 多模式串匹配算法 Trie 树和 AC 自动机 BM算法 BM算法的核心思想是通过将模式串沿着主串大踏步的向后滑动,从而大大减少比较 ...
- 【数据结构与算法】字符串匹配 BF算法 RK算法
单模式串匹配 BF 算法和 RK 算法 BM 算法和 KMP 算法 多模式串匹配算法 Trie 树和 AC 自动机 一.BF 算法 1,BF算法是Brute Force的缩写,中文译作暴力匹配算法,也 ...
- KMP算法,字符串匹配,next与nextval数组求解
目录 KMP算法简介 next数组手动求解过程 next数组求解(代码实现) 改进的KMP算法 手动求解nextval数组 nextval数组求解(代码实现) KMP算法简介 KMP算法是一种改进的字 ...
最新文章
- 为什么多 TCP 连接比单 TCP 连接传输快
- 《中国人工智能学会通讯》——2.13 医疗服务机器人综述
- C盘过满或者重装系统小技巧(不需要重做系统)
- java.util.Random 实现原理
- 【MyBatis】Mybatis的java对象名和数据库表名不同怎么办?
- “约见”面试官系列之常见面试题之第一百篇之响应路由参数的变化(建议收藏)
- Python笔记(4) 关键字
- 【C++】C++中substr的用法
- java linux和windows下文件路径间隔符的写法——解决linux下程序在windows下运行时的上传文件出错问题...
- Redis 实现队列优先级
- 车联网仿真测试解决方案
- #PLC_一次看懂数位低通滤波器设计(含代码实现)
- 嵌入式软件开发面试——一个应届生求职的亲身经历
- 世界杯为战斗民族的历史再添荣耀与光辉_数字体验_新浪博客
- 计算机应用情话,2018最新版情话大全浪漫情话 好听感人的情话
- Clion设置背景图片
- Mht制作word模板
- yum安装报错:“Could not resolve host: mirrors.aliyun.com; Unknown error“--:-- ETA Trying
- 【自学笔记】尚硅谷数据结构与算法Chapter 2 稀疏数组和队列
- c语言 pow和sqrt注意
热门文章
- VMware下Windows Server 2012添加新磁盘
- mysql 删除记录代码_mysql 删除记录时报错
- linux vue node占用内存过大,vue 大型应用内存泄漏改造经验
- net.sf.ezmorph.Morpher问题解决
- oracle安装,未找到文件 F:\app\Administrator\product\11.2.0\dbhome_2\owb\external\oc4j_ap
- Android recycleview实现混合itemview,以及recycleview添加头部尾部
- java amr 转mp3 报错_amr 转 MP3 报错it.sauronsoftware.jave.InputFormatException问
- Java秒杀系统实战系列~基于Redis的原子操作优化秒杀逻辑
- html聚光灯特效,css实现聚光灯效果的代码分享
- 利用旧手机自建anki服务器,废旧手机变身服务器,打造私人云盘