KMP 字符串搜索算法是 Knuth、Morris、Pratt 三位在类似的时间段内一起发明的一种字符串搜索算法,该算法的主要原理是利用待查找子串中的某些信息,在匹配失败时能够减少回退的步数

算法原理

假设现在有一个待搜索的字符串 ABABAC,如何利用现有的字符串实现在字符不匹配时尽可能向后调整搜索的开始位置。

目前主要存在两种处理方式:DFA 和部分匹配表

DFA[1]

假设通过原有的搜索字符串已经构建了一个 DFA,他能够帮助我们在不匹配的情况下如何移动匹配的开始位置指针。比如,将 ABABAC 构造对应的 DFA,如下图所示:

很直观的形式,当匹配过程中遇到某个不匹配的字符时,可以通过这个不匹配的字符重新定位待搜索字符串的开始位置。比如,如果查找的原字符串内容为 BCBAABACAABABAC,那么可能搜索情况如下图所示:

如果存在这样的 DFA,那么在搜索时对应的代码实现如下所示:

public int search(String txt) {
// 模拟 DFA 处理文本时进行的操作
int i, j, N = txt.length(), M = pat.length();
for (int i = 0, j = 0; i < N && j < M; ++i) {
j = dfa[txt.charAt(i)][j];
} if (j == M) return i - M;
return N;
}

DFA 的使用比较简单,但是如果需要从头开始研究如何构造对应的 DFA,这具有很大的难度。因此,这里仅仅给出构造 DFA 的代码:

public void initDFA() {
dfa[pat.charAt(0)][0] = 1;
for (int x = 0, j = 0; j < M; ++j) {
for (int c = 0; c < R; ++c) {
dfa[c][j] = dfa[c][x];
}
dfa[pat.charAt(j)][j] = j + 1;
x = dfa[pat.charAt(j)][x];
}
}

具体构造过程如下图所示:

部分匹配表[2]

首先,了解两个概念:前缀和后缀。“前缀” 是指除了字符串的最后一个字符外,子串的全部头部组合;“后缀” 是指除了首字符外,子字符串的所有尾部组合。

部分匹配表的元素是当前位置的 “前缀” 和 “后缀” 最长共有的元素的长度,比如,对于字符串 ABCDABD 来讲,它的 “部分匹配表” 的产生过程如下:

  1. “A” 的前缀和后缀都为空集,公共元素数量为 \(0\)
  2. “AB” 的前缀为 “A”,后缀为 “B”,公共元素数量为 \(0\)
  3. “ABC” 的前缀为 \([A, AB]\),后缀为 \([BC, C]\),公共元素数量为 \(0\)
  4. “ABCD” 的前缀为 \([A, AB, ABC]\),后缀为 \([BCD, CD, D]\),公共元素的数量为 \(0\)
  5. “ABCDA” 的前缀为 \([A, AB, ABC, ABCD]\),后缀为 \([BCDA, BCD, CD, A]\),公共元素为 \([A]\),长度为 \(1\)
  6. “ABCDAB” 的前缀为 \([A, AB, ABC, ABCD, ABCDA]\),后缀为 \([BCDAB, CDBAB, DAB, AB, B]\),共有的公共元素为 \([AB]\),长度为 \(2\)
  7. “ABCDABD‘ 的前缀为 \([A, AB, ABC, ABCD, ABCDA, ABCDAB]\),后缀为 \([BCDABD, CDABD, DABD, ABD, BD, D]\) 公有长度为 \(0\)

因此,最后产生的部分匹配表如下图所示:

当搜索时,通过

\[ 移动位数 = 已匹配字符数 - 对应的部分匹配值
\]

移动相应的匹配搜索开始位置

实现

  • DFA 的实现如下:

    public class KMP {
    private final String pat;
    private final int[][] dfa;
    private final int R;
    private final int m; public KMP(String pat) {
    this.pat = pat;
    R = 256;
    m = pat.length();
    dfa = new int[R][m]; dfa[pat.charAt(0)][0] = 1;
    for (int x = 0, j = 1; j < m; ++j) {
    // 复制不匹配的情况下的状态
    for (int c = 0; c < R; ++c) {
    dfa[c][j] = dfa[c][x];
    } dfa[pat.charAt(j)][j] = j + 1; // 设置匹配成功时的状态
    x = dfa[pat.charAt(j)][x]; // 更新重启状态
    }
    } public int search(String txt) {
    int n = txt.length();
    int i, j;
    for (i = 0, j = 0; i < n && j < m; ++i)
    j = dfa[txt.charAt(i)][j]; if (j == m) return i - m;
    return n;
    }
    }
  • “部分匹配表“ 的实现如下:

    import java.util.Arrays;
    import java.util.HashSet;
    import java.util.Set; public class KmpPartTable {
    private final String pat;
    private final int[] next; public KmpPartTable(String pat) {
    this.pat = pat;
    int m = pat.length(); next = new int[m];
    next[0] = 0;
    for (int i = 1; i < m; ++i) {
    Set<String> set = new HashSet<>();
    // 计算前缀集合
    for (int j = 0; j < i; ++j) {
    set.add(pat.substring(0, j));
    } // 计算后缀集合
    for (int j = 1; j <= i; ++j) {
    String str = pat.substring(j, i + 1);
    if (set.contains(str) && str.length() > next[i])
    next[i] = str.length();
    }
    } System.out.println(Arrays.toString(next));
    } public int search(String txt) {
    int m = pat.length(), n = txt.length();
    int i, j;
    for (i = 0, j = 0; i < n && j < m;) {
    int k = i, cnt = 0;
    while (k < n && j < m) {
    if (txt.charAt(k) == pat.charAt(j)) {
    k++;
    j++;
    continue;
    } cnt = (j + 1) - next[j];
    break;
    } if (j >= m) break;
    i += cnt;
    } if (j == m) return i - m;
    return n;
    }
    }

参考:

[1] 《算法(第四版)》

[2] https://www.ruanyifeng.com/blog/2013/05/Knuth–Morris–Pratt_algorithm.html

KMP 字符串搜索算法的更多相关文章

  1. grep之字符串搜索算法Boyer-Moore由浅入深(比KMP快3-5倍)

    这篇长文历时近两天终于完成了,前两天帮网站翻译一篇文章“为什么GNU grep如此之快?”,里面提及到grep速度快的一个重要原因是使用了Boyer-Moore算法作为字符串搜索算法,兴趣之下就想了解 ...

  2. grep之字符串搜索算法Boyer-Moore由浅入深(比KMP快3-5倍)(转)

    这篇长文历时近两天终于完成了,前两天帮网站翻译一篇文章“为什么GNU grep如此之快?”,里面提及到grep速度快的一个重要原因是使用了Boyer-Moore算法作为字符串搜索算法,兴趣之下就想了解 ...

  3. 字符串搜索算法Boyer-Moore

    整理日: 2015年2月16日 1. 主要特征 假设文本串text长度为n,模式串pattern长度为m,BM算法的主要特征为: 从右往左进行比较匹配(一般的字符串搜索算法如KMP都是从从左往右进行匹 ...

  4. Boyer–Moore (BM)字符串搜索算法

    在计算机科学里,Boyer-Moore字符串搜索算法是一种非常高效的字符串搜索算法.它由Bob Boyer和J Strother Moore设计于1977年.此算法仅对搜索目标字符串(关键字)进行预处 ...

  5. 从入门到精通之Boyer-Moore字符串搜索算法详解

    本文讲述的是Boyer-Moore算法,Boyer-Moore算法作为字符串搜索算法,兴趣之下就想了解这个算法,发现这个算法一开始还挺难理解的,也许是我理解能力不是很好吧,花了小半天才看懂,看懂了过后 ...

  6. KMP字符串模式匹配详解(转)

    来自CSDN     A_B_C_ABC 网友 KMP字符串模式匹配通俗点说就是一种在一个字符串中定位另一个串的高效算法.简单匹配算法的时间复杂度为O(m*n);KMP匹配算法.可以证明它的时间复杂度 ...

  7. BM和KMP字符串匹配算法学习

    BM和KMP字符串匹配算法学习 分类: 研究与学习 字符串匹配BM(Boyer-Moore)算法学习心得 http://www.cnblogs.com/a180285/archive/2011/12/ ...

  8. KMP字符串模式匹配详解(zz)

    刚看到位兄弟也贴了份KMP算法说明,但本人觉得说的不是很详细,当初我在看这个算法的时候也看的头晕昏昏的,我贴的这份也是网上找的.且听详细分解: KMP字符串模式匹配详解 来自CSDN     A_B_ ...

  9. KMP字符串模式匹配详解

    KMP字符串模式匹配详解 http://www.cppblog.com/oosky/archive/2006/07/06/9486.html

  10. BF + KMP + BM 字符串搜索算法

    BF #include <stdio.h> #include <string.h> int simplicity(char *s, char *t, int pos); int ...

随机推荐

  1. 数栈V6.0全新产品矩阵发布,数据底座 EasyMR 焕新升级

    4月20日,袋鼠云成功举行了以"数实融合,韧性生长"为主题的2023春季生长大会.会上,袋鼠云自主研发的一站式大数据基础软件--数栈V6.0产品矩阵全新发布.对旗下大数据基础平台. ...

  2. Kong入门学习实践(8)流量控制插件

    Kong的一大特色就在于强大的可扩展性,具体实现方式就是插件.一来Kong已经提供了很多内置的插件,二来我们也可以使用Lua语言自定义开发插件.今天,我们就来了解一些常用的流量控制插件. 关于流量控制 ...

  3. java---HashSet、TreeSet、泛型

    Vector 集合 的体系: –| Collection 单例集合的根接口 -–| List 如果是实现了List接口的集合类,具备的特点: 有序,可重复. ---| ArrayList ArrayL ...

  4. 最简单的一个 STL格式的网格文件

    简介 最简单格式的一个STL格式的文件 文件内容 solid filenamestl facet normal 1 1 1 outer loop vertex 0 0 1 vertex 0 1 0 v ...

  5. Feature Preserving Octree-Based Hexahedral Meshing

    Feature Preserving Octree-Based Hexahedral Meshing 论文阅读 作者映入了一种新的算法尝试去产生一个纯粹的六面体网格并且不带有自交和正值雅克比.CAD模 ...

  6. java group Layout 组框架

    简介 https://netbeans.org/features/java/swing.html 里面有一个mattise 专门用来生成布局代码 GroupLayout code 代码先不贴了

  7. 使用ETLCloud实现MySQL数据库与StarRocks数据库同步

    在现代数据架构中,数据同步是保证数据一致性和分析准确性的关键步骤之一.本文将介绍如何利用ETLCloud技术实现MySQL数据库与StarRocks数仓数据库的高效数据同步,以及其在数据管理和分析中的 ...

  8. Lazarus4Android 环境搭建

    Lazarus4Android 开发环境搭建 一.下载相关文件 最关键的就是这两个文件,其他的JDK.NDK.Java环境另说. 这里要注意,gradle版本必须小于7,不然无法使用. 二.IDE内部 ...

  9. FreeSwitch: esl inbound模式下外呼拨号

    相信大家可能接到过一些电话,听上去不象是真人打过来的,比如:通知"您的信用卡到期了",或者"您订的飞机航班取消了,请尽快改签或取消行程",这种就是所谓的&quo ...

  10. XMOJ 四月月赛 T3 旅行 题解

    我们首先尝试挖掘这个分组的性质. 我们发现,我们可以把在同一个组的夫妻和不在同一个组的夫妻分开来处理. 这里,分开之后我们只需要让一种情况有顺序,另外一种不能有顺序.如果两个没有顺序 / 有顺序的序列 ...