//字符串匹配
public class StringCmp {
//约定:A主串长 n ,B模式串 长m。要求:在A串中找到B串匹配的下标 //BM算法:从B串和A串尾部开始比较,希望一次将B串向后滑动尽量多的位数,
// 以跳过不匹配的情况,理想情况的时间复杂度是 O(n/m)
//
// A A----------*==---------
// B B---*== *为在A中的坏字符,*在B中的相同位置
// *右边的部分,2个=号表示匹配到的好后缀
// B向右滑动 B---*== 向右滑动了2位,让B坏字符位置对齐在A中相同坏字符的位置
//
// 控制B向右滑动多少位,有2个主要策略
//坏字符:在B中寻找最靠右(下标最大)的相同字符下标
//好后缀:在B中寻找最靠右(下标最大)的相同好后缀,及好后缀子串的下标
// (例如好后缀是abcd,那么好后缀子串有bcd,cd,d这3个(从右侧开始框选))
// 思路:
// 先用坏字符策略,在B串中从右往左找到下标j,若j<m,说明j的右侧有匹配成功的好后缀
// 如果没有好后缀,B向右移动坏字符在B中最大下标个位置
// 如果有好后缀, //在坏字符方式中,用于以hash方式快速找到某个坏字符在b串中的最大下标,用于做b串的快速向右滑动
private static int[] BMInitBadCharTable(char b[]) {
int[] bcoff = new int[256]; //只考虑ascii 的256个字符的情况 ,bcoff[ascii] = 该字符在b中最大下标值
for (int i = 0; i < bcoff.length; i++)
bcoff[i] = -1;
for (int i = 0; i < b.length; i++) {
bcoff[b[i]] = i;
}
return bcoff;
} //若b串只有1个字符,那么推荐用BF算法,因为判断效果都只能是一位一位的后移比对,
//BM代码所需的机器周期应该会比BF算法更多,更耗时,但是稍微复杂一点的情况BM算法效率更高
public static long BM(char[] a, char[] b) {
int[] bctab = BMInitBadCharTable(b); //坏字符缓存初始化,记录b串中每个字符最后出现的位置下标
BMInitGoodSuffix(b); //好后缀缓存初始化,记录b串中每个后缀子串最后出现的位置下标
int i = 0; //1.以i为B串的头部对齐A串的下标
int nsubmP1 = a.length - b.length + 1; //i 指向 遍历a[0,n-m+1]
while (i < nsubmP1) {
int j; //指向b串尾部 到 头部
for (j = b.length - 1; j >= 0; j--) { //2.从b串尾部开始和a串尾部比较,找到第一个坏字符
if (b[j] != a[i + j])
break; //找到了第一个坏字符,在b串中的下标=j
}
if (j < 0) //j<0表示不存在任何一个坏字符,表示和B串完全匹配成功,直接返回 在a串中的第一个匹配的下标i
return i; int k = b.length - 1 - j; //获取匹配成功的好后缀长度 ,取值范围[1, b.len-1],至少1个字符 至 最大长度-1 个
int badOff = j - bctab[a[i + j]]; //获取坏字符向后滑动量
int goodOff = 0; //好后缀向后滑动 if (j < b.length - 1) //如果有好后缀的话,也就是说坏字符j不是第一个在b串中出现,那么使用好后缀匹配规则
goodOff = getGoodOff(b.length, j, k);
int off = Math.max(goodOff, badOff);
i = i + off;
}
return -1;
} //m=b串长度,j=坏字符在b串中下标,k=好后缀匹配长度
private static int getGoodOff(int m, int j, int k) {
int goodOff = m; //1.当好后缀既没有匹配k个字符,也没有匹配k个字符的子串(prefix),那么默认向后移动整个b串长度m
if (suffix[k] != -1)
goodOff = j - suffix[k] + 1; //2.存在和当前k个好后缀匹配的情况,返回最大下标,下标应该小于等于j,所以这里goodOff至少是1~+
else { //3.若存在小于k位的子串的前缀,那么返回 r (= k-子串长度),表示b串应该向后滑动r位,从长子串到短子串遍历 (k-1 ~ 1)
for (int r = j + 2; r < m; r++) { //m-r = 好后缀位子串位数, r=b串长度-好后缀子串位数,从位数多找到位数少
if (prefix[m - r]) { // b ----j--|---------------- m长
goodOff = r; // \-r-->/ \- 好后缀 k-2 --/ 起始是-1,再多减了1是因为 prefix是从下标1开始的
break; // m-r
}
}
}
return goodOff; //可能的返回值,m,1~j(等于1~m-k),j+2~<k ,不会有负数
} static boolean[] prefix; //下标k=后缀子串字符个数,k个字符的后缀子串是否出现在B串头部
static int[] suffix; //下标k=后缀子串字符个数,k个字符的后缀子串出现在B串的除了子串本身的最靠右(最大)下标(子串本身是最靠右的,那么记录除了字串本身第二靠右的下标)
private static void BMInitGoodSuffix(char[] b) {//初始化 b串的好后缀匹配缓存,类似于 坏字符做缓存的目的,用于加速匹配过程,直接用 k(后缀长度)查找 b串中匹配的好后缀最大下标值
//初始化 //例如 b = abcde
prefix = new boolean[b.length + 1]; //从[1] 开始
suffix = new int[b.length + 1]; //[1] = e ,[2] = de, [3] = cde
for (int i = 0; i < suffix.length; i++) {
prefix[i] = false;
suffix[i] = -1;
}
int m = b.length; //下标为 好后缀的 长度 ,如果 模式串B 为 abcde ,那么当从后往前计算字串长度时 ,
// [1] = e ,[2] = de, [3] = cde = cde ... [b.length-1] = 整个b串
// (虽然不会使用到这种情况,因为如果整个b串都被匹配上了,
// 说明已经完成了字符串匹配,不会再考虑B串向右滑动的情况了)
for (int i = 0; i < m - 1; i++) { //b[0,m-1] 作为前缀子串的长度, 每个前缀子串和 与其等长到1长度的 后缀子串比较
int j = i; //指向当前前缀子串的比较字符,从尾部到头部
int k = 0; //当前和前缀子串比较的 尾部子串长度, 从0~j (也就是从1个字符开始比较,直到和当前前缀子串长度一致为止)
while (j <= 0 && b[j] == b[m - 1 - k]) { //1.如果当前长度的整个前缀子串的每个字符未完全匹配 并且 2.当前前缀子串的单独一个字符和 后缀子串 从右到左挨个比较
j--; //说明while条件判断成功,前缀子串比较字符 向左移动1个字符,准备下一个字符的比较
k++; //后缀子串 长度+1,相当于 b[m - 1 - k] 指向的字符 从b串尾向左移动一个,准备比较下一个字符
suffix[k] = j + 1; //当前k长度的后缀子串 最后一次的匹配下标, 若之后也有匹配k长度的后缀子串出现,那么 下标值较大的会覆盖之前的
}
if (j < 0) //说明当前长度的前缀子串 和同长度的 后缀子串 每个字符完全匹配,那么k长度的后缀子串就被判定为出现在前缀中
prefix[k] = true;
}
} public static void main(String[] args) {
char[] a = "abcdefg".toCharArray();
char[] b = "def".toCharArray();
System.out.println("BM(a, b) " + BM(a, b));
}
}

输出

BM(a, b) 3

如何在文本编辑器中实现时间复杂度O(n/m)的搜索功能? BM算法的更多相关文章

  1. JavaScript Iframe富文本编辑器中的光标定位

    最近在项目中碰到一个比较棘手的问题: 在iframe富文本编辑器中,有个工具栏,这个工具栏在iframe标签之外,工具栏上有一个按钮,点击该按钮向iframe正在编辑中的光标处插入一个图片,图片会插入 ...

  2. php 解析富文本编辑器中的hmtl内容,富文本样式正确输出

    说明:富文本编辑器中的内容在直接获获取后需要解析以后才能在页面中正确显示 我在后端这样处理: $content = htmlspecialchars_decode($info['intro']); h ...

  3. 字符串匹配Boyer-Moore算法:文本编辑器中的查找功能是如何实现的?---这应该讲的最容易懂的文章了!

    关于字符串匹配算法有很多,之前我有讲过一篇 KMP 匹配算法:图解字符串匹配 KMP 算法,不懂 kmp 的建议看下,写的还不错,这个算法虽然很牛逼,但在实际中用的并不是特别多.至于选择哪一种字符串匹 ...

  4. 富文本编辑器UEditor自定义工具栏(三、自定义工具栏功能按钮图标及工具栏样式简单修改)

    导读 富文本编辑器UEditor提供丰富了定制配置项,如果想设置个性化的工具栏按钮图标有无办法呢?答案是肯定的!前两篇博文简要介绍了通过将原工具栏隐藏,在自定义的外部按钮上,调用UEditor各命令实 ...

  5. 过滤富文本编辑器中的html元素和其他元素

    https://blog.csdn.net/fjssharpsword/article/details/53467079 1.应用场景:从一份html文件中或从String(是html内容)中提取纯文 ...

  6. 如何在文本编辑器中实现搜索功能? 字符串比较算法 BF算法 RK算法

    1.暴力比较 BF算法 2.比较字串hash值 RK算法 //字符串匹配 public class StringCmp { //约定:A主串长 n ,B模式串 长m.要求:在A串中找到B串匹配的下标 ...

  7. Java文本编辑器中遇到的问题详解

    今天介绍文件的读取和写入,分别用FileReader,FileWriter 1,FileWriter类(字符输出流类) 构造方法:FileWriter fw = new FileWriter(Stri ...

  8. 对于富文本编辑器中使用lazyload图片懒加载

    使用lazyload.js图片懒加载的作用是给用户一个好的浏览体验,同时对服务器减轻了压力,当用户浏览到该图片的时候再对图片进行加载,项目中使用lazyload的时候需要将图片加入data-orgin ...

  9. WPF datagrid/gridcontrol 中选中多行,复制粘贴到excel或其他文本编辑器中

    wpf中 data grid 开启自带的选中,然后复制,可以到excel中直接粘贴,在某些业务场景中很实用,方便.开启也很简单: SelectionMode="Row" 加上这个, ...

随机推荐

  1. IT兄弟连 HTML5教程 DIV+CSS网站首页布局示例

    首页的设计直接影响网站的整体形象,虽然没有一个统一的规范,但最好将其设计为大众化的,只要信息内容能够合理地编排即可,使用户可以方便地找到需要的信息.另外,首页的高度最好不要超过三个屏幕,页面中使用的颜 ...

  2. Web安全测试学习笔记-DVWA-盲注(使用sqlmap)

    之前的sql注入页面(https://www.cnblogs.com/sallyzhang/p/11843291.html),返回了查询结果和错误信息.而下面的页面,返回信息只有存在和不存在两种情况, ...

  3. Java8新特性之接口defualt,static方法

    简介 作用 Java8中接口引入了defualt,static两种方法提供默认实现,彻底打破了接口不能有默认实现的规定 static 让接口类似于工具类,提供一些静态方法 static方法不会被子类继 ...

  4. HTML入门(列表、表单、常用表单控件、浮动框架、iframe、 摘要与细节、度量标签)

    一.列表 1.作用:默认显示方式为从上到下的显示数据 2.列表的组成 列表类型和列表项 3.列表的分类:有序列表   无序列表   自定义列表 无序列表语法为ul>li, 语法:ul代表列表,l ...

  5. springboot传值踩坑

    由于我现在写的项目都是前后端分离的,前端用的是vue,后端springboot,于是前后端传值的问题就是一个比较重要的问题,为此我还特意去学了一下vue的传值,其实就是用一个axios组件,其实就是基 ...

  6. Dynamics 365中的公告(Post)分析

    我是微软Dynamics 365 & Power Platform方面的工程师罗勇,也是2015年7月到2018年6月连续三年Dynamics CRM/Business Solutions方面 ...

  7. react学习之js-xlsx导入和导出excel表格

    前记:最近真的挺忙的,一件事接着一件,都忘了我的React项目,尽管这是一个没写概率没写离散的夜晚,我决定还是先做做我的React 好了,进入正题 项目需求,需要导入和导出表单,发现前端已经强大到无所 ...

  8. Windows系统Git配置教程(Git配置git config)

    Windows系统Git配置教程(Git配置git config)   在很多Git配置教程中,多是安装完毕后就告诉大家要配置用户名和邮箱,但是这个配置是保存在哪里呢,配置后面的参数有什么不同呢,下面 ...

  9. 任正非讲话稿 PDF整理版

    任正非讲话稿 PDF整理版 任正非思想之路 这里收录了任正非讲话稿400余篇,从1994年到2018年,从深圳.中国到东南亚.非洲.欧洲.美洲,从研发.市场.服务到财经.人力资源.战略.内控与公共关系 ...

  10. [Go] golang定时器与redis结合

    golang定时器与redis结合,每隔1秒ping一下,每隔20秒llen一下队列的长度 package main import ( "fmt" "time" ...