暴力匹配字符串的升级版算法 —— Kmp算法

2024-05-05 22:28

本文主要是介绍暴力匹配字符串的升级版算法 —— Kmp算法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

文章目录

  • 一、Kmp算法是什么?
  • 二、算法分析
    • 1.构建next数组
    • 2.匹配主串
  • 三、完整代码


一、Kmp算法是什么?

简单来说,KMP(Knuth-Morris-Pratt)算法主要用于解决字符串匹配问题。也就是当你有一个主串(text)和一个模式串(pattern)时,KMP算法可以在主串中快速找到模式串的出现位置。其核心思想是利用已经部分匹配的信息来避免不必要的匹配尝试。
相对于我们最开始使用的暴力匹配两个字符串是否相等的时间复杂度大大降低。、
上面说道 KMP 算法主要是通过消除主串指针的回溯来提高匹配的效率的,那么,它是则呢样来消除回溯的呢?就是因为它提取并运用了加速匹配的信息!

二、算法分析

1.构建next数组

KMP需要next数组的辅助,那么它是如何来生成的呢?可以采用递推的方式进行快速求解,利用已经掌握的信息来避免重复的运算。
其中next数组是使用匹配串进行构建出来的,它通过使用一个preCommonLen的变量来记录这个字符串的共同公共前缀。
在这里插入图片描述
在这里插入图片描述
根据上面得出,这个next就是记录了存放这个数组前后具有相同的前后缀

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在生成呢next[ ]的过程中,如果遇到不一样的字符该怎么办呢?

​ 其实可以找出不一样的字符的前一位,例如上面的 C与B不相同,那么就找最近的A重新比较,也就是左边字符串的后缀A

在这里插入图片描述
因为这里A的下标为 1 ,跳过一位,那么就是从B开始判断; 则右边的从B开始进行重新比较

所以蓝色箭头的值与黄色箭头的值一样,所以重新有共同的字串,则黄色箭头的值为 2

就这样我们就完成了Kmp算法的核心:构建next[ ]数组

 // todo 构建next数组private static int[] buildedString(String patternString/*匹配串,不是主串*/,int[] arrayPatternNext) {int prefix_len=0;// 共同前缀int i=1;//从下标1开始,因为第0位前缀为0char[] chars=patternString.toCharArray();while (i<patternString.length()){if (chars[prefix_len]==chars[i]){prefix_len+=1;arrayPatternNext[i]=prefix_len;i+=1;}else {if (prefix_len==0){//没有公共前缀arrayPatternNext[i]=0;i+=1;}else prefix_len=arrayPatternNext[prefix_len-1];// 不相等且有公共前缀,那么需要根据next数组来更新公共前缀}}return arrayPatternNext;}

2.匹配主串

步骤:

1 i 下标是不会回溯的,只会往前;

2 如果两个字符串的相同下标的比较字符相等的话,就进行向下移动;

3 当匹配中有不一样的字符时,就会去找next【】数组的相同子串后缀下标的值,并进行跳过多少位。
在这里插入图片描述

例如这里跳过了 2 位,所以子串下标 j = 2 ,指在了【0,1,2】第三号元素进行下一次的重新匹配,完美的跳过了上一次的重复字符,避免了回溯带来的时间损耗,这个就是KMP算法的魅力了。
在这里插入图片描述


// 字符串的匹配private static int getCommonString(String a/*主串*/, String patternString, int[] arrayPatternNext) {int i=0;int j=0;while (i<a.length()){// 主串的下标一直往前走,则时间复杂度为线性if (a.charAt(i)==patternString.charAt(j)){i+=1;j+=1;}else if (j>0){//因为当前面不匹配的时候,这个匹配串的下标就需要根据next数组作出调整j=arrayPatternNext[j-1];}else i+=1; //不相等,字串下标也没有动,主串下标就往前走if (j==patternString.length()-1){ //模式串的j到达了末尾commonLen=i-j+1;// 直接计算长度并返回break;}}return commonLen;}

三、完整代码

import java.io.*;
import java.util.Arrays;
import java.util.Scanner;public class Kmp {static  int commonLen=0;public static void main(String[] args) throws IOException {BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));System.out.println("输入主串:");String a=bufferedReader.readLine();System.out.println("输入匹配串");String patternString=bufferedReader.readLine();if (a.length()<patternString.length()) System.out.println("主串需要大于等于匹配串");else {int[] arrayPatternNext=new int[patternString.length()];Arrays.fill(arrayPatternNext,0);arrayPatternNext=buildedString(patternString,arrayPatternNext);System.out.println(getCommonString(a,patternString,arrayPatternNext)==0?"主串没有找到匹配串":"主串存在该匹配字串");}}private static int getCommonString(String a, String patternString, int[] arrayPatternNext) {int i=0;int j=0;while (i<a.length()){// 主串的下标一直往前走,则时间复杂度为线性if (a.charAt(i)==patternString.charAt(j)){i+=1;j+=1;}else if (j>0){//因为当前面不匹配的时候,这个匹配串的下标就需要根据next数组作出调整j=arrayPatternNext[j-1];}else i+=1; //不相等,字串下标也没有动,主串下标就往前走if (j==patternString.length()-1){commonLen=i-j+1;break;}}return commonLen;}// todo 构建next数组private static int[] buildedString(String patternString,int[] arrayPatternNext) {int prefix_len=0;// 共同前缀int i=1;char[] chars=patternString.toCharArray();while (i<patternString.length()){if (chars[prefix_len]==chars[i]){prefix_len+=1;arrayPatternNext[i]=prefix_len;i+=1;}else {if (prefix_len==0){//没有公共前缀arrayPatternNext[i]=0;i+=1;}else prefix_len=arrayPatternNext[prefix_len-1];}}return arrayPatternNext;}
}

这篇关于暴力匹配字符串的升级版算法 —— Kmp算法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/962824

相关文章

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

关于Gateway路由匹配规则解读

《关于Gateway路由匹配规则解读》本文详细介绍了SpringCloudGateway的路由匹配规则,包括基本概念、常用属性、实际应用以及注意事项,路由匹配规则决定了请求如何被转发到目标服务,是Ga... 目录Gateway路由匹配规则一、基本概念二、常用属性三、实际应用四、注意事项总结Gateway路由

C#从XmlDocument提取完整字符串的方法

《C#从XmlDocument提取完整字符串的方法》文章介绍了两种生成格式化XML字符串的方法,方法一使用`XmlDocument`的`OuterXml`属性,但输出的XML字符串不带格式,可读性差,... 方法1:通过XMLDocument的OuterXml属性,见XmlDocument类该方法获得的xm

JSON字符串转成java的Map对象详细步骤

《JSON字符串转成java的Map对象详细步骤》:本文主要介绍如何将JSON字符串转换为Java对象的步骤,包括定义Element类、使用Jackson库解析JSON和添加依赖,文中通过代码介绍... 目录步骤 1: 定义 Element 类步骤 2: 使用 Jackson 库解析 jsON步骤 3: 添

Java 字符数组转字符串的常用方法

《Java字符数组转字符串的常用方法》文章总结了在Java中将字符数组转换为字符串的几种常用方法,包括使用String构造函数、String.valueOf()方法、StringBuilder以及A... 目录1. 使用String构造函数1.1 基本转换方法1.2 注意事项2. 使用String.valu

Python中的随机森林算法与实战

《Python中的随机森林算法与实战》本文详细介绍了随机森林算法,包括其原理、实现步骤、分类和回归案例,并讨论了其优点和缺点,通过面向对象编程实现了一个简单的随机森林模型,并应用于鸢尾花分类和波士顿房... 目录1、随机森林算法概述2、随机森林的原理3、实现步骤4、分类案例:使用随机森林预测鸢尾花品种4.1

python修改字符串值的三种方法

《python修改字符串值的三种方法》本文主要介绍了python修改字符串值的三种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学... 目录第一种方法:第二种方法:第三种方法:在python中,字符串对象是不可变类型,所以我们没办法直接

JAVA中整型数组、字符串数组、整型数和字符串 的创建与转换的方法

《JAVA中整型数组、字符串数组、整型数和字符串的创建与转换的方法》本文介绍了Java中字符串、字符数组和整型数组的创建方法,以及它们之间的转换方法,还详细讲解了字符串中的一些常用方法,如index... 目录一、字符串、字符数组和整型数组的创建1、字符串的创建方法1.1 通过引用字符数组来创建字符串1.2

C#中字符串分割的多种方式

《C#中字符串分割的多种方式》在C#编程语言中,字符串处理是日常开发中不可或缺的一部分,字符串分割是处理文本数据时常用的操作,它允许我们将一个长字符串分解成多个子字符串,本文给大家介绍了C#中字符串分... 目录1. 使用 string.Split2. 使用正则表达式 (Regex.Split)3. 使用