Java 数据结构与算法系列精讲之KMP算法

 更新时间:2022年02月18日 09:15:58   作者:我是小白呀  
在很多地方也都经常看到讲解KMP算法的文章,看久了好像也知道是怎么一回事,但总感觉有些地方自己还是没有完全懂明白。这两天花了点时间总结一下,有点小体会,我希望可以通过我自己的语言来把这个算法的一些细节梳理清楚,也算是考验一下自己有真正理解这个算法

概述

从今天开始, 小白我将带大家开启 Java 数据结构 & 算法的新篇章.

KMP 算法

KMP (Knuth-Morris-Pratt), 是一种改进的字符串匹配算法. KMP 算法解决了暴力匹配需要高频回退的问题, KMP 算法在匹配上若干字符后, 字符串位置不需要回退, 从而大大提高效率. 如图:

举个例子 (字符串 “abcabcdef” 匹配字符串 “abcdef”):

次数 暴力匹配 KMP 算法 说明
1 abcabcdef abcdef abcabcdef abcdef a 和 a 匹配
2 abcabcdef abcdef abcabcdef abcdef ab 和 ab 匹配
3 abcabcdef abcdef abcabcdef abcdef abc 和 abc 匹配
4 abcabcdef abcdef abcabcdef abcdef abca 和 abcd 不匹配, 回退. 暴力匹配回退到索引 1, 即 “b”, KMP 算法索引跳置 3, 即 “a”
5 abcabcdef abcdef abcabcdef abcdef 暴力匹配 b 和 a 不匹配, 后移. KMP 算法 a 和 a 匹配
6 abcabcdef abcdef abcabcdef abcdef 暴力匹配 c 和 a 不匹配, 后移. KMP 算法 ab 和 ab 匹配
7 abcabcdef abcdef abcabcdef abcdef 暴力匹配 a 和 a 匹配. KMP 算法 abc 和 abc 匹配
8 abcabcdef abcdef abcabcdef abcdef 暴力匹配 ab 和 ab 匹配. KMP 算法 abcd 和 abcd 匹配
9 abcabcdef abcdef abcabcdef abcdef 暴力匹配 abc 和 abc 匹配. KMP 算法 abcde 和 abcde 匹配
10 abcabcdef abcdef abcabcdef abcdef 暴力匹配 abcd 和 abcd 匹配. KMP 算法 abcdef 和 abcdef 匹配 , 匹配完成
11 abcabcdef abcdef abcabcdef abcdef 暴力匹配 abcde 和 abcde 匹配. KMP 算法匹配完成
12 abcabcdef abcdef abcabcdef abcdef 暴力匹配 abcd 和 abcd 匹配, 匹配完成. KMP 算法匹配完成

部分匹配表

部分匹配表 (Partial Match Table) 指的是 “前缀” 和 “后缀” 的最长共有元素的长度.

举个例子, 字符串 “ABCDABD” 的前缀与后缀:

字符串 前缀 后缀 共同部分
A NaN NaN NaN 0
AB A B NaN 0
ABC A, AB C, BC NaN 0
ABCD A, AB, ABC D, CD, BCD NaN 0
ABCDA A, AB, ABC, ABCD A, DA, CDA, BCDA A 1
ABCDAB A, AB, ABC, ABCD, ABCDA B, AB, DAB, CDAB, BCDAB AB 2
ABCDAB A, AB, ABC, ABCD, ABCDA, ABCDAB D, BD, ABD, DABD, CDABD, BCDABD NaN 0

KMP 算法实现

重点:

KMP 算法中移动的位数 = 已匹配的字符数 - 对应的部分匹配值

import java.util.Arrays;

public class KMPMatch {

    public static int Match(String str1, String str2, int[] next) {

        // 初始化索引
        int i = 0;
        int j = 0;

        for (; i < str1.length(); i++) {

            if (j > 0 && str1.charAt(i) != str2.charAt(j)) {
                // 不匹配, 回退
                i = i - next[j - 1];
                j = 0;
            }

            // 匹配
            if (str1.charAt(i) == str2.charAt(j)) {
                j++;
            }

            // 返回索引
            if (j == str2.length()) {
                return i - j + 1;
            }
        }
        return -1;
    }

    // 部分匹配
    public static int[] getNext(String s) {

        // 定义数组
        int next[] = new int[s.length()];

        // 初始化i, j
        int i = 0;
        int j = -1;
        next[0] = -1;

        // 遍历
        while (i < s.length() - 1) {
            if (j == -1 || s.charAt(i) == s.charAt(j)) {
                // 匹配成功
                next[i] = j + 1;
                i++;
                j++;
            } else {
                //一旦不匹配成功j回退到-1
                j = -1;
            }
        }
        return next;
    }


    public static void main(String[] args) {

        // 字符串1
        String str1 = "BBCABCDAB ABCDABD";

        // 字符串2
        String str2 = "ABCDABD";

        // 匹配表
        int[] next = getNext(str2);
        System.out.println(Arrays.toString(next));


        // KMP算法
        int result = Match(str1, str2, next);
        System.out.println(result);
    }
}

输出结果:

[0, 0, 0, 0, 1, 2, 0]
10

到此这篇关于Java 数据结构与算法系列精讲之KMP算法的文章就介绍到这了,更多相关Java KMP 算法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Mybatis 复杂对象resultMap的使用

    Mybatis 复杂对象resultMap的使用

    这篇文章主要介绍了Mybatis 复杂对象resultMap的使用,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • Java并发编程中构建自定义同步工具

    Java并发编程中构建自定义同步工具

    这篇文章主要介绍了Java并发编程中构建自定义同步工具,本文讲解了可阻塞状态依赖操作的结构、有界缓存实现基类示例、阻塞实现方式一:抛异常给调用者、阻塞实现方式二:通过轮询和休眠、阻塞实现方式三:条件队列等内容,需要的朋友可以参考下
    2015-04-04
  • spring cloud 之 客户端负载均衡Ribbon深入理解

    spring cloud 之 客户端负载均衡Ribbon深入理解

    下面小编就为大家带来一篇spring cloud 之 客户端负载均衡Ribbon深入理解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-06-06
  • 百度Java面试题 前200页精选(中)

    百度Java面试题 前200页精选(中)

    这篇文章主要为大家分享了Java面试资源中篇,百度“Java面试题”前200页都在这里了,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-12-12
  • 使用@Value值注入及配置文件组件扫描

    使用@Value值注入及配置文件组件扫描

    这篇文章主要介绍了使用@Value值注入及配置文件组件扫描方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • springmvc直接不经过controller访问WEB-INF中的页面问题

    springmvc直接不经过controller访问WEB-INF中的页面问题

    这篇文章主要介绍了springmvc直接不经过controller访问WEB-INF中的页面问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-02-02
  • 关于Java中如何实现文件的读写操作

    关于Java中如何实现文件的读写操作

    在Java中,可以使用File和FileInputStream、FileOutputStream、BufferedReader、PrintWriter等类来进行文件读写操作,需要的朋友可以参考下
    2023-05-05
  • Java中 shuffle 算法的使用

    Java中 shuffle 算法的使用

    本篇文章,小编将为大家介绍,在Java中 shuffle 算法的使用,有需要的朋友可以参考一下
    2013-04-04
  • Mybatis实体类对象入参查询的笔记

    Mybatis实体类对象入参查询的笔记

    这篇文章主要介绍了Mybatis实体类对象入参查询的笔记,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • mybatis-plus雪花算法生成Id使用详解

    mybatis-plus雪花算法生成Id使用详解

    本文主要介绍了mybatis-plus雪花算法生成Id使用详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07

最新评论