字符串匹配基础

作者: TomGui | 来源:发表于2020-06-22 22:35 被阅读0次

BF算法

BF 算法中的 BF 是 Brute Force 的缩写,中文叫作暴力匹配算法,也叫朴素匹配算法。

我们在主串中,检查起始位置分别是0、1、2…n-m且长度为m的n-m+1个子串,看有没有跟模式串匹配的。最坏情况时间复杂度是 O(n*m)。

RK 算法

RK 算法的全称叫 Rabin-Karp 算法,是由它的两位发明者 Rabin 和 Karp 的名字来命名的。

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

BM(Boyer-Moore)算法

BM 算法原理分析

1.坏字符规则

BM算法的匹配顺序比较特别,它是按照模式串下标从大到小的顺序,倒着匹配的。从模式串的末尾往前倒着匹配,当发现某个字符没法匹配的时候。把这个没有匹配的字符叫作坏字符(主串中的字符)。

2.好后缀规则

所谓某个字符串s的后缀子串,就是最后一个字符跟s对齐的子串,比如abc的后缀子串就包括c, bc。所谓前缀子串,就是起始字符跟s对齐的子串,比如abc的前缀子串有a,ab。我们从好后缀的后缀子串中,找一个最长的并且能跟模式串的前缀子串匹配的,假设是{v},然后将模式串滑动到如图所示的位置。

原则

当模式串和主串中的某个字符不匹配的时候,如何选择用好后缀规则还是坏字符规则,来计算模式串往后滑动的位数?

我们可以分别计算好后缀和坏字符往后滑动的位数,然后取两个数中最大的,作为模式串往后滑动的位数。这种处理方法还可以避免我们前面提到的,根据坏字符规则,计算得到的往后滑动的位数,有可能是负数的情况。

BM算法代码实现

public class BM {
    private static final int SIZE = 256; // 全局变量或成员变量

    private void generateBC(char[] b, int m, int[] bc) {
        for (int i = 0; i < SIZE; ++i) {
            bc[i] = -1; // 初始化bc
        }
        for (int i = 0; i < m; ++i) {
            int ascii = (int) b[i]; // 计算 b[i] 的 ASCII 值
            bc[ascii] = i;
        }
    }

    // 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;
    }

    // b 表示模式串,m 表示长度,suffix,prefix 数组事先申请好了
    private void generateGS(char[] b, int m, int[] suffix, boolean[] prefix) {
        for (int i = 0; i < m; ++i) { // 初始化
            suffix[i] = -1;
            prefix[i] = false;
        }
        for (int i = 0; i < m - 1; ++i) { // b[0, i]
            int j = i;
            int k = 0; // 公共后缀子串长度
            while (j >= 0 && b[j] == b[m - 1 - k]) { // 与 b[0, m-1] 求公共后缀子串
                --j;
                ++k;
                suffix[k] = j + 1; // j+1 表示公共后缀子串在 b[0, i] 中的起始下标
            }

            if (j == -1)
                prefix[k] = true; // 如果公共后缀子串也是模式串的前缀子串
        }
    }

}

相关文章

  • 正则表达式

    基础语法 元字符^ 匹配行或者字符串的起始位置$ 匹配行或者字符串的结束位置\s 匹配空格\d 匹配数字\w 匹配...

  • iOS 字符串操作

    iOS之字符串截取、替换、分隔、匹配 其他基础操作

  • 6.stringr处理字符串

    一、字符串基础 二、正则表达进行模式匹配 三、具体使用 1.匹配检测 2.提取匹配内容 3.分组匹配 4.替换匹配...

  • python全栈 part2 - 004 前端补遗,Django

    前端基础知识补遗 JS正则 test: 判断字符串是否符合规定; exec: 获取匹配的数据; 多行匹配: 默认就...

  • shell编程

    1、正则表达式作用:匹配文中的字符串 1.1、基础正则表达式基础元字符*:匹配前一个字符0次或任意多次.:匹配除换...

  • 大数据算法系列9:字符串匹配问题,海量字符串处理

    一. 字符串匹配 1.1 字符串匹配 字符串匹配:字符串匹配在实际工作中经常遇到,但是我们经常使用的是编程语言自带...

  • Python算法-字符串(String)

    字符串匹配问题字符串匹配(String Matching):又称为模式匹配(Pattern Matching)。可...

  • 字符串匹配 —— KMP算法

    基础设置 字符串相关操作 KMP 模式匹配算法 通过计算返回子串T的next数组字符串T[0]中是存储的字符串长度...

  • 正则表达式(不全)

    正则表达式(基础但内容大量) 单个字符串来描述、匹配一系列匹配某个句法规则的字符串。由简单字符与特殊字符组合 [理...

  • 字符串匹配基础

    BF算法 BF 算法中的 BF 是 Brute Force 的缩写,中文叫作暴力匹配算法,也叫朴素匹配算法。 我们...

网友评论

    本文标题:字符串匹配基础

    本文链接:https://www.haomeiwen.com/subject/nfhqfktx.html