字符串匹配算法知多少?

文章目录

  • BF算法
  • RK算法
  • 编辑器中的全局替换方法:BM算法
    • 坏字符
    • 好后缀规则
    • 代码实现
  • KMP算法

一说到字符串匹配算法,不知道会有多少小伙伴不由自主的想起那个kmp算法呢?

想到是很正常的,谁让它那么优秀呢。


BF算法

不要被事物的表面现象所迷惑,这个算法全称:Brute Force,有个拉风的中文名:暴力匹配算法。

能想明白了吧。

如果模式串长度为 m,主串长度为 n,那在主串中,就会有 n-m 1 个长度为 m 的子串,我们只需要暴力地对比这 n-m 1 个子串与模式串,就可以找出主串与模式串匹配的子串。

1、从头开始往后遍历匹配; 2、遇上不对了,就回头,把子串和主串的匹配头后移一位 3、重复以上。直到找到或确定找不到。

  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

复杂度很高啊,但是在实际开发中也是比较常用的。为什么呢?
真当天天都有成千上万个字符的主串让我们去匹配吗?一般都比较短,而且,统计意义上,算法执行效率不会真的到M*N的地步。

理论还是要结合实际的。

还有另一个原因,就是它好写。当然kmp现在更好写,因为封装好了。
我说的是类似的场景,没有封装好的函数时候,好写,好改。


RK算法

RK 算法的思路是这样的:我们通过哈希算法对主串中的 n-m 1 个子串分别求哈希值,然后逐个与模式串的哈希值比较大小。如果某个子串的哈希值与模式串相等,那就说明对应的子串和模式串匹配了(这里先不考虑哈希冲突的问题,后面我们会讲到)。因为哈希值是一个数字,数字之间比较是否相等是非常快速的,所以模式串和子串比较的效率就提高了。

有没有方法可以提高哈希算法计算子串哈希值的效率呢?

我们假设要匹配的字符串的字符集中只包含 K 个字符,我们可以用一个 K 进制数来表示一个子串,这个 K 进制数转化成十进制数,作为子串的哈希值。

比如要处理的字符串只包含 a~z 这 26 个小写字母,那我们就用二十六进制来表示一个字符串。我们把 a~z 这 26 个字符映射到 0~25 这 26 个数字,a 就表示 0,b 就表示 1,以此类推,z 表示 25。

这里有一个小细节需要注意,那就是 26^(m-1) 这部分的计算,我们可以通过查表的方法来提高效率。我们事先计算好 26^0、26^1、26^2……26^(m-1),并且存储在一个长度为 m 的数组中

模式串哈希值与每个子串哈希值之间的比较的时间复杂度是 O(1),总共需要比较 n-m 1 个子串的哈希值,所以,这部分的时间复杂度也是 O(n)。所以,RK 算法整体的时间复杂度就是 O(n)。

但是呢,还有一个很致命的问题,叫做数值过大。
以幂增的速度是非常快的,用不了多久int就hold不住了啊,那要怎么办?难道我们前面所做的努力都白费了?

其实不然。
比方说我们可以改乘为加,当我们匹配到一样的哈希值的时候,再打开子串进行比对,因为相加的话是会有哈西冲突的。

此外,我们还可以加点优化,一边对主串构建,一边对子串进行匹配,如果一样的话就不继续计算后面的hash了。
该省的时候就要省,该花的时候就要花。


编辑器中的全局替换方法:BM算法

用过吗?比方说要在我这篇博客里找出全部的“主串”这个词,有没有想过其底层的原理?

这是一个性能优于KMP的算法。

坏字符

BM 算法的匹配顺序比较特别,它是按照模式串下标从大到小的顺序,倒着匹配的。

我们从模式串的末尾往前倒着匹配,当我们发现某个字符没法匹配的时候。我们把这个没有匹配的字符叫作坏字符(主串中的字符)

这时候该如何操作呢?我们去子串中寻找这个坏字符,如果找到了,就让两个字符的位置对上,继续往后,如果没有找到,就将整个子串移动到坏字符后面。

很显然,这会儿没找到。

接下来该怎么滑呢?又是个坏字符。
但是在子串中找到了那个坏字符,那就将两个字符的位置对上。

模式串中有对应的坏字符时,让模式串中 最靠右 的对应字符与坏字符相对。

但是呢,用这个规则还是不太够用的,有些个特殊情况吧,它会导致不但不会向后滑动模式串,还有可能会倒推、

比如说主串:kkkkkkkkkkkkkkkkkk,模式串是 akk


好后缀规则

如果模式串中存在已经匹配成功的好后缀,则把目标串与好后缀对齐,然后从模式串的最尾元素开始往前匹配。

如果无法找到匹配好的后缀,找一个匹配的最长的前缀,让目标串与最长的前缀对齐:

如果完全不存在和好后缀匹配的子串,则右移整个模式串


代码实现

难顶,我一定会回来的

// a,b 表示主串和模式串;n,m 表示主串和模式串的长度。
public int bm(char[] a, int n, char[] b, int m) {
  int[] bc = new int[SIZE]; // 记录模式串中每个字符最后出现的位置
  generateBC(b, m, bc); // 构建坏字符哈希表
  int[] suffix = new int[m];
  boolean[] prefix = new boolean[m];
  generateGS(b, m, suffix, prefix);
  int i = 0; // j 表示主串与模式串匹配的第一个字符
  while (i <= n - m) {
    int j;
    for (j = m - 1; j >= 0; --j) { // 模式串从后往前匹配
      if (a[i j] != b[j]) break; // 坏字符对应模式串中的下标是 j
    }
    if (j < 0) {
      return i; // 匹配成功,返回主串与模式串第一个匹配的字符的位置
    }
    int x = j - bc[(int)a[i j]];
    int y = 0;
    if (j < m-1) { // 如果有好后缀的话
      y = moveByGS(j, m, suffix, prefix);
    }
    i = i   Math.max(x, y);
  }
  return -1;
}

// j 表示坏字符对应的模式串中的字符下标 ; m 表示模式串长度
private int moveByGS(int j, int m, 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-1;   r) {
    if (prefix[m-r] == true) {
      return r;
    }
  }
  return m;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

KMP算法

【C 】算法集锦(10)通俗讲kmp算法

(0)

相关推荐

  • 字符串硬核讲解

    字符串硬核讲解

  • 字符串匹配算法详解

    希望看到文章的你们,能够在今年的研究生考试中超常发挥. 愿你们都能考上自己心仪的学校,为你们的备考生涯划上一个完美的句号.做为你们的师兄有几句话想对你们说,希望这些话能对你们有一些帮助. 马上就要考试 ...

  • 那些经典算法:字符串匹配算法BM算法

    单模式串匹配算法中BM(Boyer-Moore)算法算是很难理解的算法了,不过性能高效,据说比KMP算法性能提升3到4倍,suricata里面的单模式匹配就是用这种算法,所以有必要学习下,再把suri ...

  • Go 数据结构和算法篇(十二):字符串匹配之 KMP 算法

    昨天 以下文章来源于xueyuanjun ,作者xueyuanjun xueyuanjun学院君的订阅号,我会在这里持续更新优质全栈编程技术教程,包括但不限于 Golang.PHP.JavaScrip ...

  • 见病知方,经方高手16张经方的用药经验

    2020年飞快的过去了!我的中医学习实践却在徘徊中慢行.翻看病案记录,最早的一个是在2月14日.也许还有更早的.遗憾的是,有些没有记录,有些没有下文.盘点自己用过的方子,算是对过去一年的小结,也算是对 ...

  • 知乎10条神回复,针针见血,看完整个人通透多了

    作者|读者来源|读者(ID:duzheweixin) 在我们的一生中, 总会遇到各种各样的问题. 有时候, 让我们苦苦追寻的人生答案, 其实就在我们的身边-- 01 问:命运是什么? 神回复:命,是弱 ...

  • 你究竟是不是“脾虚”体质?看一看嘴唇就知...

    你究竟是不是"脾虚"体质?看一看嘴唇就知道了!   中医认为"脾其华在唇",健康的唇色应该是颜色红润.均匀的,上下嘴唇的颜色无差异,也没有明显的边线. 并且整个 ...

  • 知之与结局

    对男人一知半解的女人, 最终都成了男人的妻子, 对男人无所不知的女人, 最终都成了孤身老女人.

  • 5个良心的装机必备软件,知乎超10w人推荐,错过太可惜

    新买了电脑,不知道该装什么软件? 别纠结了,今天给大家梳理了几款实用的必备软件,都是知乎上很多人推荐且热度高的产品,话不多说,上软件! 一.录制神器-ScreenToGIF ScreenToGif是一 ...

  • 心内科主任建议:中老年人一定要知道的8种...

    心内科主任建议:中老年人一定要知道的8种心脏自我诊断方法,务必学习! 1.劳力性呼吸困难. 常在中等活动量时就感到明显气短,呼吸浅表而短促,休息后可恢复. 这种呼吸困难是心脏功能不全尤其左心功能不全时 ...

  • 空腹血糖高,选什么药?餐后血糖高,选什么药?一看便知!

    糖尿病病友选药要根据自己的血糖水平,基础血糖高和餐后血糖高,或者两者均高,选药方案各不同.如果是基础血糖高,应选择降基础血糖的药物:如果是餐后血糖高,则应选择降餐后血糖的药物,而且降餐后血糖的药物不能 ...