java 实现KMP算法

Java实现KMP算法完整攻略

什么是KMP算法

KMP算法全称是Knuth-Morris-Pratt算法,是一个字符串查找算法,用于在一个字符串S中查找一个模式串P出现的位置。

KMP算法思想

KMP算法的思想是通过一个”部分匹配”的概念,当部分匹配发生后,可以知道一部分字符是匹配的,从而充分利用这个已知信息,避免从头再去比较已经比较过的字符。

KMP算法流程

KMP算法分为两个步骤:第一步是预处理,第二步是匹配。

第一步:预处理

预处理目的是得到一个数组next,用于在匹配时根据已经匹配的字符来确定下一步移动的位置。该数组的计算方式如下:

void getNext(int[] next, String pattern) {
    int j = 0;
    int k = -1;
    next[0] = -1;
    while (j < pattern.length() - 1) {
        if (k == -1 || pattern.charAt(j) == pattern.charAt(k)) {
            j++;
            k++;
            next[j] = k;
        } else {
            k = next[k];
        }
    }
}

上述代码中,j表示匹配串的下标,k表示前缀串的下标。在计算next数组时,从第1个位置开始比较,k初始化为-1,表示前缀串不存在。如果当前j和k位置的字符匹配,则j和k都加1,同时将next[j]设置为k,表示在j位置匹配失败后,应该跳到k位置重新尝试匹配。如果当前字符不匹配,则将k更新为next[k],继续尝试匹配。直到j到达字符串的长度减一处为止。

第二步:匹配

匹配时,通过已经预处理得到的next数组,来决定下一步移动的位置。匹配过程中,让j指向匹配字符串的第i个位置,k指向模式串的第0个位置,此时开始对j和k所指的字符串进行匹配。

int kmp(String s, String pattern) {
    int i = 0;
    int j = 0;
    int[] next = new int[pattern.length()];
    getNext(next, pattern);
    while (i < s.length() && j < pattern.length()) {
        if (j == -1 || s.charAt(i) == pattern.charAt(j)) {
            i++;
            j++;
        } else {
            j = next[j];
        }
    }
    if (j == pattern.length()) {
        return i - j;
    } else {
        return -1;
    }
}

上述代码中,i表示s的下标,j表示pattern的下标。如果当前s[i]和pattern[j]匹配,则i和j都加1。如果不匹配,则将j跳到next[j]的位置重新尝试匹配。如果j达到pattern的长度,则说明匹配成功,返回i-j的值。

示例

下面是两个示例,用来说明KMP算法的使用场景。

示例一

我们有两个字符串s和p:

s: ababababc
p: ababc

我们要在s中查找p是否出现。这时可以使用KMP算法。

预处理得到的next数组为:

p: a b a b c
i: -1 0 0 1 2
next: -1 0 0 1 2

下面是使用KMP算法查找p在s中出现的代码:

String s = "ababababc";
String p = "ababc";
int index = kmp(s, p);
if (index != -1) {
    System.out.println("p在s中从" + index + "开始出现");
} else {
    System.out.println("p不在s中出现");
}

运行代码后,可以得到输出结果:

p在s中从2开始出现

示例二

我们有一个字符串s,想要在中间进行插入,使其成为一个回文字符串。插入的字符可以是任意字符。

s: abca

如果使用暴力算法,需要将所有的插入情况都试一遍,时间复杂度为O(n^2)。而如果使用KMP算法,可以在预处理时将s倒序,然后将s和s的逆序拼接成一个新的字符串,这样就可以得到最大前缀回文子串和最大后缀回文子串。

预处理得到的next数组为:

s: a b c a
i: 0 1 2 3
next: 0 0 0 1

逆序得到的next数组为:

s: a c b a
i: 0 1 2 3
next: 0 1 0 0

拼接得到的next数组为:

s: a b c a a c b a
i: 0 1 2 3 4 5 6 7
next: 0 0 0 1 0 1 0 0

可以看到,拼接得到的next数组中,最后一个位置的值就是最大前缀回文子串和最大后缀回文子串的长度。

下面是使用KMP算法插入回文字符串的代码:

String s = "abca";
int[] next = new int[s.length()];
getNext(next, s);
int len = next[s.length() - 1];
StringBuilder sb = new StringBuilder(s);
for (int i = 0; i < len; i++) {
    sb.append(s.charAt(len - i - 1));
}
System.out.println(sb.toString());

运行代码后,可以得到输出结果:

abcacba

总结

本文详细讲解了KMP算法的原理和实现,展示了两个使用场景的示例代码。KMP算法可以应用于各种需要字符串查找和匹配的场合,是一种非常实用的算法。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java 实现KMP算法 - Python技术站

(0)
上一篇 2023年5月18日
下一篇 2023年5月18日

相关文章

  • Java反转字符串和相关字符编码的问题解决

    下面我将为你详细讲解Java反转字符串和相关字符编码的问题解决的完整攻略。 1. 反转字符串 Java反转字符串有多种方法,以下是两种示例。 1.1 使用StringBuilder String str = "hello world"; StringBuilder sb = new StringBuilder(str); String r…

    Java 2023年5月20日
    00
  • Spring Data JPA框架的Repository自定义实现详解

    下面就是关于Spring Data JPA框架的Repository自定义实现的详细攻略。 介绍 Spring Data JPA 是 Spring 框架的一部分,它提供了一种简单的方法来访问关系型数据库中的数据。它使用JPA规范来访问数据库,简化了与数据库的交互,大大减少了操作数据库的代码量。在 Spring Data JPA 中,我们可以使用 Reposi…

    Java 2023年6月3日
    00
  • SpringBoot2 整合 ClickHouse数据库案例解析

    下面我将为你详细讲解如何实现SpringBoot2整合ClickHouse数据库的步骤。 准备工作 安装ClickHouse数据库 创建一个SpringBoot2项目 添加依赖 在SpringBoot2项目的pom.xml文件中添加ClickHouse驱动依赖: <dependency> <groupId>cc.blynk</g…

    Java 2023年5月20日
    00
  • 基于Cookie使用过滤器实现客户每次访问只登录一次

    概述 使用过滤器来实现客户端每次访问只登录一次,需要使用Cookie来保存会话信息。把用户的登录状态作为一个标识存储到Cookie中,通过过滤器来检查Cookie中是否存在标识,如果存在则表示用户已经登录过,直接放行请求;如果不存在,则表示用户未登录或者会话已失效,需要跳转到登录界面进行身份验证。 实现步骤 2.1 配置过滤器 在web.xml文件中添加如下…

    Java 2023年6月16日
    00
  • 一文带你弄懂Java中线程池的原理

    一文带你弄懂Java中线程池的原理 线程池的概念 线程池是指一组预先创建好的线程,可以被程序反复使用,用于执行多个任务。线程池的好处在于可以管理线程数量、重用线程以及减少线程创建和销毁的开销。 在Java中,线程池相关的类都位于java.util.concurrent包中。 线程池的组成 线程池主要由以下几个组成部分: 线程池管理器(ThreadPoolEx…

    Java 2023年5月19日
    00
  • Java中关于char类型变量能够输出中文的问题

    Java中的char类型变量能够输出中文,是因为Java使用的是Unicode字符编码标准,其中全球所有的字符都有唯一的码位,包括中文字符。在Java中,char类型变量以16位无符号整数形式存储字符。由于Unicode字符集在编码范围内包含了中文字符,所以Java的char类型变量和String类型能将中文字符完美输出。 在Java中,对于char类型变量…

    Java 2023年5月26日
    00
  • SpringBoot参数校验之@Validated的使用详解

    下面就为大家详细讲解“SpringBoot参数校验之@Validated的使用详解”。 什么是@Validated 在Spring框架中,我们经常需要对方法入参的校验,以保证参数的正确性。 SpringBoot基于Hibernate Validator,为开发者提供了方便简洁的实现方式:@Validated。 @Validated 用于校验方法入参,可以将该…

    Java 2023年5月20日
    00
  • JavaScript学习笔记整理_setTimeout的应用

    首先让我们来详细讲解“JavaScript学习笔记整理_setTimeout的应用”这个主题的完整攻略。 简介 setTimeout() 是 JavaScript 的一个函数,它可以在一定时间后执行指定的函数或代码。通过 setTimeout() 函数,我们可以实现倒计时、延迟显示等功能。 语法 setTimeout() 函数的语法如下: setTimeou…

    Java 2023年6月15日
    00
合作推广
合作推广
分享本页
返回顶部