Java笔记——KMP算法

news2024/11/18 6:40:16

KMP算法

文章目录

  • KMP算法
    • KMP算法介绍
    • 主要逻辑
    • Next数组
    • KMP搜索
    • 代码解释
      • 生成next数组
      • 模式串匹配
    • 源码展示

KMP算法介绍

KMP算法是一种串的模式匹配算法,用来求子串在主串的位置。是数据结构中比较难的一种算法。KMP算法的核心在于点在于如何利用子串生成next数组,和如何利用next数组来寻找子串在主串的位置

主要逻辑

在模式串匹配的过程中,一般的思路是利用两个指针,分别指向主串和模式串(子串),然后从头比较,如果两个指针指向的字符是一样的,就将两个指针同时后移,接着比较后面的字符。当两个指针指向的字符不再一样的时候,就要将指向主串元素的指针回溯到一定位置,同时指向模式串元素的指针也回溯到一定位置,然后重新比较。

当指向模式串元素的指针遍历完模式串的同时,恰好遍历完主串或者没有遍历完主串,则说明已经找到模式串在主串中的位置
匹配成功

当指向模式串元素的指针没有遍历完模式串的时候已经把主串遍历完了,则说明模式串在主串中不存在匹配的位置

匹配失败

这种思路最主要的地方在于如何让模式串右移的过程做到高效,此时就有了两种思路,一种是简单的BF算法(暴力算法)一种就是KMP算法

BF算法主要是最小程度的将模式串右移,从而达到不遗漏任何一个可能的目的。在模式串匹配的工程中,当主串上的一个字符和模式串不匹配的时候,就将指向主串元素的指针回溯到主串起始比较的下一个元素,指向模式串元素的指针回溯到模式串最开始元素,然后继续开始比较。这种思路主要每次比较失败都将模式串右移一个字符的长度,时间复杂度是O(n*m)

KMP算法相比于BF算法主要的特点是利用next数组提高模式串比较的效率。在模式串比较的过程中,根据已经生成next数组将将模式串最大限度的右移。在将模式串右移的过程中,还做到了指向主串元素的指针不回溯,按照next数组将指向模式串数组的指针进行回溯。时间复杂度为O(n+m)
KMP

Next数组

当我们在进行字符串匹配的BF算法过程中,因为在一遍一遍的遍历模式串才会导致时间复杂度那么高。当我们如果可以遍历完一遍模式串以后将模式串的规律存储到一个数组中,然后根据这个数组中存储的数据进行回溯的话,就会大大降低时间复杂度,这就是为什么要生成next数组的原因。

一个人能能走的多远不在于他在顺境时能走的多快,而在于他在逆境时多久能找到曾经的自己。 ——KMP

Next数组是KMP算法的核心,Next数组决定了如何将模式串做到最大程度的右移,在next数组中存储的数据代表着如果当前字符模式串匹配失败,指向模式串元素的指针要回溯的位置。这个回溯的位置一般是看模式串中相同的字符串前缀和字符串后缀,当一个字符串前缀和后缀一致的时候,就会进行按照字符串前缀和字符串后缀的长度进行生成next数组的数值。

在一个字符串中,它有字符串前缀和字符串后缀,前缀是指最后一个字符以外,一个字符串的全部头部的组合,后缀是指第一个字符以外,一个字符串的全部尾部组合。就以字符串"ababc",它的字符串前缀有a,ab,aba,abab,它的字符串后缀有c,bc,abc,babc,把每一个字符串前缀都当成一个独立的字符串,寻找每个字符串的公共前后缀。‘a’,‘ab’没有公共前后缀,"aba"存在公共前后缀,是’a’,所以生成的模式匹配值是1,“abab"存在公共前后串,是"ab”,所以生成的模式匹配值是2。最后字符串"ababac"生成的next数组是00120,生成好Next数组就可以根据这个数组进行KMP模式串匹配

KMP搜索

KMP搜索核心点之一就是如何利用next数组,已知next数组是根据模式串的规律进行生成的,如何利用这个规律将模式串匹配做到更快更高效就是KMP算法的一个关键。

根据Next数组的介绍可以得知,next的数值代表着最长模式串前缀子串的公共前后缀的最长长度,同时也代表着要回溯的位置。当模式串和主串的元素不再一致的时候,就要根据next数据将指向模式串字符的指针进行回溯,回溯的位置就是next数组的值。当next数组此时的值为0的时候,就要根据情况进行一遍检查。

代码解释

生成next数组

    //获取到一个字符串的部分匹配值
    public static int[] KMP_next(String dest) {
        //创建一个数组next,保存部分匹配值。长度跟模式串长度一致
        int[] next = new int[dest.length()];
        next[0] = 0;//如果字符串是长度为1 部分匹配值就是0
        //当字符串的长度为1的时候,没有前缀也没有后缀,更不可能有公共前后缀,所以默认是0
        //i从第二位开始,j从第一位开始,遍历整个模式串
        for (int i = 1, j = 0; i < dest.length(); i++) {
            //当dest.charAt(j) != dest.charAt(i),我们需要从next[j-1]获取新的j
            //知道我们发现有dest.charAt(j) == dest.charAt(i)成立才停止
            while (j > 0 && dest.charAt(j) != dest.charAt(i)) {
                //这里设置j>0是为了防止陷入死循环中(i指向的元素不等于第一个元素,并且j)
                j = next[j - 1];//当est.charAt(j) != dest.charAt(i),进行一个回溯,一直到dest.charAt(j) == dest.charAt(i)或者j已经指向第一个元素的位置
            }
            //当dest.charAt(j) == dest.charAt(i)满足时,部分匹配值就是+1
            if (dest.charAt(j) == dest.charAt(i)) {
                j++;//这里进行j++,刚好让j进一位,但是还是在i后面
            }
            next[i] = j;//将j的位置放进next数组中
        }
        return next;
    }

模式串匹配

	//KMP搜索算法
    public static int KmpSearch(String str1, String str2) {
        //调用KMP_next方法生成一个next数组
        int[] next = KMP_next(str2);
        //遍历主串
        for (int i = 0, j = 0; i < str1.length(); i++) {
            //这里也是一个回溯,但是这里的回溯是根据next数组,将指向模式串元素的指针回溯到指定位置,从而实现模式串的右移。
            if (j > 0 && str1.charAt(i) != str2.charAt(j)){
                j = next[j - 1];
            }
            //如果模式串和主串的被指向的元素相同就将j后移,循环结束后i会自动后移一位,所以这里就不用写i++
            if (str1.charAt(i) == str2.charAt(j)) {
                j++;
            }
            //判断j是不是已经指向模式串最后一个字符,如果指向了模式串最后一个字符,就说明已经在主串中找到跟模式串相同的子串,此时直接根据i,j就可以确定子串在主串中位置
            if (j == str2.length()) {
                return i - j + 1;
            }
        }
        //当主串遍历完成但是没有提前return的时候,说明没有模式串匹配失败,没有在主串中找到和模式串相同的子串,此时就要返回一个主串中不存在的位置用来说明没有找到,一般用-1来说明
        return -1;
    }

for循环里面的三个if判断不能调换顺序,判断j是否指向模式串最后一个元素的位置的if判断必须放到后面,因为放到开头时会发生i已经自增一次的情况,return语句也要相应的发生变化。

源码展示

import java.util.Arrays;

public class KMP {
    public static void main(String[] args) {
        String str1 = "BBC ABCDAB ABCDABCDABDE";
        String str2 = "ABCDABA";
        int[] next = KMP_next(str2);
        System.out.println("next=" + Arrays.toString(next));
        int index=KmpSearch(str1,str2);
        System.out.println(index);
    }

    //KMP搜索算法
    public static int KmpSearch(String str1, String str2) {
        int[] next = KMP_next(str2);
        //遍历
        for (int i = 0, j = 0; i < str1.length(); i++) {
            if (j>0&&str1.charAt(i)!=str2.charAt(j)){
                j=next[j-1];
            }
            if (str1.charAt(i) == str2.charAt(j)) {
                j++;
            }
            if (j == str2.length()) {
                return i - j + 1;
            }
        }
        return -1;
    }

    //获取到一个字符串的部分匹配值
    public static int[] KMP_next(String dest) {
        //创建一个数组next,保存部分匹配值
        int[] next = new int[dest.length()];
        next[0] = 0;//如果字符串是长度为1 部分匹配值就是0
        for (int i = 1, j = 0; i < dest.length(); i++) {
            //当dest.charAt(j) != dest.charAt(i),我们需要从next[j-1]获取新的j
            //知道我们发现有dest.charAt(j) == dest.charAt(i)成立才停止
            while (j > 0 && dest.charAt(j) != dest.charAt(i)) {
                j = next[j - 1];
            }
            //当dest.charAt(j) == dest.charAt(i)满足时,部分匹配值就是+1
            if (dest.charAt(j) == dest.charAt(i)) {
                j++;
            }
            next[i] = j;
        }
        return next;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/570098.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vim的使用、vim入门的三种常用模式、以及vim中常用的命令(超详细)

vim 入门的三种常用模式&#xff1a;分别是 1. 命令模式、2. 插入/编辑模式、3. 底行模式 1. 命令模式 控制屏幕光标的移动&#xff0c;字符、字或行的删除&#xff0c;移动复制某区段及进入Insert mode下&#xff0c;或者到 last line mode 如下&#xff0c;这个就是命令模式…

Numpy入门看这一篇就够了【史上入门最简单,开袋即食】

一边学习一边分享&#xff0c;好记性不如烂笔头 目录 一边学习一边分享&#xff0c;好记性不如烂笔头 NumPy问题思考&#xff1a; numpy是什么&#xff1f; 为什么要学习numpy&#xff1f; numpy是怎么组成的&#xff1f;特点是什么&#xff1f; numpy的应用场景有哪些&a…

css定位模式

1. 为什么需要定位&#xff1f; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"…

自动化专业求职方向与前景分析(合集)

自动化专业求职方向与前景分析 自动化专业求职方向 自动化专业是近几年高校教育改革中几个控制类专业合并后形成的宽口径专业&#xff0c;其实自动化就是搞控制的&#xff0c;用老师的话说就是控制一切可以控制的物理量&#xff0c;还说学自动化的人都要有控制的欲望。所谓控制…

Augmented Language Models(增强语言模型)

Augmented Language Models: A Survey 先上地址&#xff1a;https://arxiv.org/pdf/2302.07842.pdf 概率论难以支撑通用人工智能技术的诞生。—— Yann LeCun LLMs取得的巨大进展不再多说&#xff0c;它目前被诟病最多的问题是其会提供非事实但看似可信答案&#xff0c;即幻觉…

数组排序——从荷兰国旗问题到快速排序

本文首先将会介绍荷兰国旗问题&#xff0c;再讲述如何从该问题过渡到快速排序。 荷兰国旗问题 荷兰国旗问题&#xff08;Dutch National Flag Problem&#xff09;是由荷兰计算机科学家Edsger Dijkstra所提出&#xff0c;该问题的描述如下&#xff1a; 给定n个红、白、蓝三种颜…

JNDI学习笔记

最近在研究JNDI注入漏洞&#xff0c;就先浅浅的学习以下JNDI相关知识。 JNDI对各种目录服务的实现进行抽象和统一化。 在 Java 应用中除了以常规方式使用名称服务(比如使用 DNS 解析域名)&#xff0c;另一个常见的用法是使用目录服务作为对象存储的系统&#xff0c;即用目录服务…

SpringBoot --- 基础篇

一、快速上手SpringBoot 1.1、概述 SpringBoot开发团队认为原始的Spring程序初始搭建的时候可能有些繁琐&#xff0c;这个过程是可以简化的&#xff0c;那原始的Spring程序初始搭建过程都包含哪些东西了呢&#xff1f;为什么觉得繁琐呢&#xff1f;最基本的Spring程序至少有一…

大数据:VMware | Ubuntu | Hadoop | Spark | VMwaretools | Python 安装配置总结

一.环境概述 Linux发行版&#xff1a;Ubuntu虚拟机应用&#xff1a;VMware Workstation ProHadoop版本&#xff1a;3.1.3|伪分布式集群JDK版本&#xff1a;JDK1.8.0_162Spark版本:2.4.0Scala版本:2.12.8Python版本:3.6.8 | 3.7.16 二.Ubuntu 2.1 光盘文件 首先进入链接Down…

因为AI,我被裁了;MJ设计海报全流程;独立开发者每周收入2.3K美元;MJ常用参数超详细介绍 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f916; 受 AI 影响&#xff0c;这 8 家公司开始裁员…… 为了搞清楚 AI 最近在影响哪些行业、哪些职业&#xff0c;作者花了三天事件找到了八…

基于SSM的网络在线考试系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 前言…

《Java并发编程实战》课程笔记(二)

可见性、原子性和有序性问题&#xff1a;并发编程 Bug 的源头 源头之一&#xff1a;缓存导致的可见性问题 在单核时代&#xff0c;所有的线程都是在一颗 CPU 上执行&#xff0c;CPU 缓存与内存的数据一致性容易解决。 因为所有线程都是操作同一个 CPU 的缓存&#xff0c;一个…

《面试1v1》ThreadLocal

我是 javapub&#xff0c;一名 Markdown 程序员从&#x1f468;‍&#x1f4bb;&#xff0c;八股文种子选手。 面试官&#xff1a; 你好&#xff0c;请问你对 ThreadLocal 有了解吗&#xff1f; 候选人&#xff1a; 您好&#xff0c;我知道 ThreadLocal 是一个 Java 中的类&a…

【坐标变换】坐标系坐标变换简单推导--未完待续

如图所示&#xff0c;假设已知坐标系 ( X , Y ) (X,Y) (X,Y)&#xff0c;旋转后的坐标系为 ( X ′ , Y ′ ) (X,Y) (X′,Y′)&#xff0c;旋转角度为 θ \theta θ&#xff0c;假设点p在 ( X , Y ) (X,Y) (X,Y)坐标系下为 ( x , y ) (x,y) (x,y)&#xff0c;坐标在旋转后的坐标…

速来!谷歌师兄的LeetCode刷题笔记开源了!

有小伙伴私聊我说刚开始刷LeetCode的时候&#xff0c;感到很吃力&#xff0c;刷题效率很低。我以前刷题的时候也遇到这个问题&#xff0c;直到后来看到这个谷歌师兄总结的刷题笔记&#xff0c;发现LeetCode刷题都是套路呀&#xff0c;掌握这些套路之后&#xff0c;就变得非常简…

kubernetes高可用+harbor高可用

kubernetes高可用harbor高可用 基于kubeadm安装kubernetes高可用集群全部主机环境初始化双主节点部署keepalive双主节点初始化kubeadm在k8smaster1节点上初始化k8s在k8smaster2节点上做扩容操作 harbor高可用集群初始化harbor1节点安装环境在另一台节点上配置使用私有harbor仓库…

初学QT:使用QtDesigner绘制一个简单的界面(Day01)

关于Qt 打算在这里记录我学习qt过程中遇见的问题的收获 今天是学习qt的第一天&#xff0c;首先找了一个界面打算照着这个界面写一个一样的 因为是第一天&#xff0c;所以我用的是qt designer写的 其中遇到的问题&#xff1a; 设置背景图片 首先不能直接添加图片到背景图片中…

如何在华为OD机试中获得满分?Java实现【分界线】一文详解!

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 &#x1f31f;专栏地址: Java华为OD机试真题&#xff08;2022&2023) 文章目录 1. 题目描述2. 输入描述3. 输出描述…

在 Alma Linux 9 上安装 Node.js 的 3 种不同方法

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时&#xff0c;用于构建快速、可扩展的网络应用程序。在 Alma Linux 9 上安装 Node.js 可以为开发者提供强大的工具和库来开发服务器端应用程序。 本文将介绍三种不同的方法来安装 Node.js 在 Alma Linux 9 上。 1. 方法一…

LLMs的自动化工具系统(HuggingGPT、AutoGPT、WebGPT、WebCPM)

在前面两篇博文中已经粗略介绍了增强语言模型和Tool Learning&#xff0c;本篇文章看四篇代表性的自动化框架&#xff0c;HuggingGPT、AutoGPT、WebGPT、WebCPM。 Augmented Language Models&#xff08;增强语言模型&#xff09;Toolformer and Tool Learning&#xff08;LLM…