字符串查找之 KMP 算法思路讲解和代码实现

2023-11-04

算法介绍

KMP 算法是一种改进的字符串匹配算法,由 D.E.Knuth,J.H.Morris 和 V.R.Pratt 提出的。KMP 算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的,KMP 算法的时间复杂度 O(m+n)。

相同前后缀

KMP 算法之所以能够快速匹配,主要是因为在迭代过程中并不是逐个查找,而是根据模式串的最长相同前后缀表进行跳跃查找。这里的相同前后缀指的是字符串中前缀和后缀相同的子串,不包括整个字符串

示例 1:“aba”,“a”既是“aba”的前缀,也是“aba”的后缀,所以“a”就是“aba”的相同前后缀。
示例 2:“ababa”,“a”是“ababa”的相同前后缀,“aba”也是“ababa”的相同前后缀。

下面演示“abacabad”的最长相同前后缀表是如何得到的。

i
0
1
2
3
4
5
6
7
子串
a
ab
aba
abac
abaca
abacab
abacaba
abacabad
最长相同前后缀
-
-
a
-
a
ab
aba
-
table
0
0
1
0
1
2
3
0

查找演示

看看 KMP 算法如何使用最长相同前后缀表进行查找。

字符串,“abacabaabacabad”
模式串,“abacabad”

匹配图解
i 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
字符串 a✅ b✅ a✅ c✅ a✅ b✅ a✅ a❎ b a c a b a d
模式串 a✅ b✅ a✅ c✅ a✅ b✅ a✅ d❎
j 0 1 2 3 4 5 6 7
最长前后缀表 0 0 1 0 1 2 3 0

字符串和模式串前 7 个字符都能匹配上,当 i=7,j=7 时,匹配失败。如果是暴力查找,那么就需要让 i=1,j=0,然后重新开始匹配,如下:

i 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
字符串 a b❓ a c a b a a b a c a b a d
模式串 a❓ b a c a b a d
j 0 1 2 3 4 5 6 7

如果是 KMP 算法会是怎样呢?
直接将模式串已成功匹配的前缀“aba”和字符串已成功匹配的“aba”对齐,让 i=7,j=3,然后继续匹配。如下:

i 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
字符串 a b a c a✅ b✅ a✅ a❓ b a c a b a d
模式串 a✅ b✅ a✅ c❓ a b a d
j 0 1 2 3 4 5 6 7
最长前后缀表 0 0 1 0 1 2 3 0

因为只有模式串成功匹配的子串“abacaba”中的某个前缀和字符串中已经匹配成功的子串“abacaba”中的某个后缀相同,后续的比较才有意义,而且只有取最长的相同前后缀才不会遗漏可能的匹配,而“aba”就是“abacaba”的最长相同前后缀。

i=7,j=3 匹配失败,所以继续将模式串已成功匹配的前缀“a”和字符串已成功匹配的“a”对齐,让 i=7,j=1,然后继续匹配。如下:

i 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
字符串 a b a c a b a✅ a❓ b a c a b a d
模式串 a✅ b❓ a c a b a d
j 0 1 2 3 4 5 6 7
最长前后缀表 0 0 1 0 1 2 3 0

i=7,j=1 匹配失败,因为已成功匹配的子串没有相同前后缀,所以将模式串整体后移和字符串未匹配成功的字符对齐,让 i=7,j=0,然后继续匹配下去。如下:

i 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
字符串 a b a c a b a a✅ b✅ a✅ c✅ a✅ b✅ a✅ d✅
模式串 a✅ b✅ a✅ c✅ a✅ b✅ a✅ d✅
j 0 1 2 3 4 5 6 7
最长前后缀表 0 0 1 0 1 2 3 0

最终匹配完成,从第一第二阶段来看,KMP 算法比暴力查找算法少了很多无用的循环,所以效率比较比暴力查找自然快多了。注:KMP 算法时间复杂度是 O(m+n),暴力查找是 O(m*n)。

算法实现

public int strStr(String text, String pattern) {
    if ("".equals(pattern)) {
        return 0;
    }
    int[] table = new int[pattern.length()];
    int i = 0, j = 1;
    while (j < table.length) {
        if (pattern.charAt(i) == pattern.charAt(j)) {
            table[j++] = i + 1;
            i++;
        } else {
            if (i == 0) {
                j++;
            } else {
                i = table[i - 1];
            }
        }
    }

    i = 0;
    j = 0;
    while (i < text.length()) {
        if (text.charAt(i) == pattern.charAt(j)) {
            i++;
            j++;
        } else {
            if (j == 0) {
                i++;
            } else {
                j = table[j - 1];
            }
        }
        if (j == pattern.length()) {
            return i - j;
        }
    }
    return -1;
}

视频讲解

  1. 在 YouTube 上查看请点击https://youtu.be/kUaiHxkX7lY
  2. 在 B 站上查看请点击https://www.bilibili.com/video/BV1bi4y1x7As
  3. 在西瓜视频上查看请点击https://www.ixigua.com/i6840806957645824516
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

字符串查找之 KMP 算法思路讲解和代码实现 的相关文章

  • 唯一索引或主键违规:“PRIMARY KEY ON PUBLIC.xxx”; SQL语句

    每当我的应用程序启动时 我都会收到以下错误消息 Caused by org h2 jdbc JdbcSQLException Unique index or primary key violation PRIMARY KEY ON PUBL
  • 菜单未显示在应用程序中

    由于某种原因 我的操作菜单在我的 Android Studio 应用程序中消失了 我正在按照教程学习如何创建 Android 应用程序 但最终遇到了这个问题 我正在使用 atm 的教程 http www raywenderlich com
  • Java 中的 XPath 节点集

    我在 eclipse 中有这段代码 NodeSet nodes NodeSet xPath evaluate expression inputSource XPathConstants NODESET 它给我 NodeSet 上的编译时错误
  • AES 加密 Java/plsql

    我需要在Java和plsql DBMS CRYPTO for Oracle 10g 上实现相同的加密 解密应用程序 两种实现都工作正常 但这里的问题是我对相同纯文本的加密得到了不同的输出 下面是用于加密 解密过程的代码 Java 和 PLS
  • Java程序中的数组奇怪的行为[重复]

    这个问题在这里已经有答案了 我遇到了这个 Java 程序及其以意想不到的方式运行 以下程序计算 int 数组中元素对之间的差异 import java util public class SetTest public static void
  • 在浏览器中点击应用程序时播放框架挂起

    我正在 Play 中运行一个应用程序activator run 也许 5 次中有 3 次 它会挂起 当我去http localhost 9000 它就永远坐在那里旋转 我看到很多promise timed out错误也 我应该去哪里寻找这个
  • java.io.IOException: %1 不是有效的 Win32 应用程序

    我正在尝试对 XML 文档进行数字签名 为此我有两个选择 有一个由爱沙尼亚认证中心为程序员创建的库 还有一个由银行制作的运行 Java 代码的脚本 如果使用官方 认证中心 库 那么一切都会像魅力一样进行一些调整 但是当涉及到银行脚本时 它会
  • 使用替换字符串中多个单词的最有效方法[重复]

    这个问题在这里已经有答案了 此刻我正在做 Example line replaceAll replaceAll cat dog replaceAll football rugby 我觉得那很丑 不确定有更好的方法吗 也许循环遍历哈希图 ED
  • 序列化对象以进行单元测试

    假设在单元测试中我需要一个对象 其中所有 50 个字段都设置了一些值 我不想手动设置所有这些字段 因为这需要时间而且很烦人 不知何故 我需要获得一个实例 其中所有字段都由一些非空值初始化 我有一个想法 如果我要调试一些代码 在某个时候我会得
  • 在具有相同属性名称的不同数据类型上使用 ModelMapper

    我有两节课说Animal AnimalDto我想用ModelMapper将 Entity 转换为 DTO 反之亦然 但是对于具有相似名称的一些属性 这些类应该具有不同的数据类型 我该如何实现这一目标 动物 java public class
  • Java中接口作为方法参数

    前几天去面试 被问到了这样的问题 问 反转链表 给出以下代码 public class ReverseList interface NodeList int getItem NodeList nextNode void reverse No
  • 反思 Groovy 脚本中声明的函数

    有没有一种方法可以获取 Groovy 脚本中声明的函数的反射数据 该脚本已通过GroovyShell目的 具体来说 我想枚举脚本中的函数并访问附加到它们的注释 Put this到 Groovy 脚本的最后一行 它将作为脚本的返回值 a la
  • 制作java包

    我的 Java 类组织变得有点混乱 所以我要回顾一下我在 Java 学习中跳过的东西 类路径 我无法安静地将心爱的类编译到我为它们创建的包中 这是我的文件夹层次结构 com david Greet java greeter SayHello
  • Java中未绑定通配符泛型的用途和要点是什么?

    我不明白未绑定通配符泛型有什么用 具有上限的绑定通配符泛型 stuff for Object item stuff System out println item Since PrintStream println 可以处理所有引用类型 通
  • Android JNI C 简单追加函数

    我想制作一个简单的函数 返回两个字符串的值 基本上 java public native String getAppendedString String name c jstring Java com example hellojni He
  • 如何配置eclipse以保持这种代码格式?

    以下代码来自 playframework 2 0 的示例 Display the dashboard public static Result index return ok dashboard render Project findInv
  • 查看Jasper报告执行的SQL

    运行 Jasper 报表 其中 SQL 嵌入到报表文件 jrxml 中 时 是否可以看到执行的 SQL 理想情况下 我还想查看替换每个 P 占位符的值 Cheers Don JasperReports 使用 Jakarta Commons
  • 如何测试 spring-security-oauth2 资源服务器安全性?

    随着 Spring Security 4 的发布改进了对测试的支持 http docs spring io spring security site docs 4 0 x reference htmlsingle test我想更新我当前的
  • com.jcraft.jsch.JSchException:身份验证失败

    当我从本地磁盘上传文件到远程服务器时 出现这样的异常 com jcraft jsch JSchException Auth fail at org apache tools ant taskdefs optional ssh Scp exe
  • Jackson 将单个项目反序列化到列表中

    我正在尝试使用一项服务 该服务为我提供了一个带有数组字段的实体 id 23233 items name item 1 name item 2 但是 当数组包含单个项目时 将返回该项目本身 而不是包含一个元素的数组 id 43567 item

随机推荐

  • 音频格式_想入坑HIFI?你得先了解这些——音频格式篇

    闲暇时戴上耳机 任旋律静静流淌 无论是忧郁的琴曲 还是律动的电音 都能带给人独特的享受 喜爱听歌的你对音频格式了解多少呢 这些傻傻分不清楚的格式又该如何区分 为什么需要高音质音源 一套最基本的音频系统涵盖了四个部分 每一个部分都缺一不可 如
  • 网络编程8/15——TCP服务器模型(多进程并发、多线程并发),TCP和UDP的本地通信(域套接字)

    目录 多进程并发服务器 模型 代码 多线程并发服务器 模型 代码 TCP本地通信 服务器 客户端 UDP本地通信 服务器 客户端 多进程并发服务器 模型 void handler int sig 回收僵尸进程 回收成功则再回收一次 直到回收
  • 论述奇偶校验和海明码

    一 奇偶校验 奇偶校验码是奇校验码和偶校验码的统称 是一种检错码 用于检查二进制数据的位错 并且用1个比特位来标记校验结果 所以当我们的数据有n位时 要传输给接收端的数据有n 1位 采用奇校验时 若所要传输的数据 含有奇数个1 则校验位为0
  • LM 系列开关电源芯片

    LM3477 High Efficiency High Side N Channel Controller 312 2006 2 18 3 03 59 LM3477A High Efficiency High Side N Channel
  • 经典网络结构梳理:Mobilenet网络结构

    论文下载地址 https arxiv org abs 1704 04861 Caffe复现地址 https github com shicai MobileNet Caffe Mobilenet发布在2017年的CVPR Mobilenet
  • CURL命令

    生成一个ca证书 openssl pkcs12 in test p12 out test crt 使用证书访问 curl cert test p12 cert type P12 cacert test crt header content
  • unity进阶--xml的使用学习笔记

    文章目录 xml实例 解析方法一 解析方法二 xml path 创建xml文档 xml实例 解析方法一 解析方法二 xml path 创建xml文档
  • 利用 RDMA 技术加速 Ceph 存储解决方案

    利用 RDMA 技术加速 Ceph 存储解决方案 晓兵XB 云原生云 2023 04 29 20 37 发表于四川 首发链接 利用 RDMA 技术加速 Ceph 存储解决方案 在本文中 我们首先回顾了 Ceph 4K I O 工作负载中遇到
  • Linux内核:系统调用大全(持续更新中)

    系统调用 1 sys brk 1 sys brk 系统调用sys brk的函数原型 sys brk 是一个操作系统调用 用于更改进程的堆空间大小 sys brk 函数接收一个无符号长整型参数brk 表示要求的新的程序数据段 堆 结束地址 如
  • Kubernetes 之深入理解 DaemonSet

    文章目录 Daemon Pod 的 过人之处 Daemon Pod 的定义 如何保证每个 Node 只有一个被管理的 Pod 何为 Toleration DaemonSet 是一个非常简单的控制器 DaemonSet 的使用方法 Daemo
  • 博思得标签打印机驱动_博思得V6驱动

    博思得Postek V6标签打印机驱动是博思得Postek品牌旗下V6型号标签打印机使用的驱动程序 这款驱动程序可以为您解决标签打印机连接不上电脑的情况 并且可以为您解决两者之间的故障 使用更便捷 博思得Postek V6标签打印机驱动安装
  • Appium自动化框架从0到1之 Driver配置封装

    不管是调用模拟器 还是调用真机 都需要准备一些driver的参数 以便被调用 思想 我们把driver配置信息 封装到yaml文件 然后通过读取yaml文件的内容 调用其driver信息 为了更直观的看如何封装 我们直接上代码 caps y
  • shell单双引号嵌套+变量

    metadata annotations volume kubernetes io selected node TARGET NODE
  • 云计算中微服务是什么Java之命名、标示符、变量

    微服务架构是一种架构模式 它提倡将单一应用程序划分成一组小的服务 服务之间相互协调 互相配合 为用户提供最终价值 每个服务运行在其独立的进程中 服务和服务之间采用轻量级的通信机制相互沟通 每个服务都围绕着具体的业务进行构建 并且能够被独立的
  • 【笔试强训选择题】Day34.习题(错题)解析

    作者简介 大家好 我是未央 博客首页 未央 303 系列专栏 笔试强训选择题 每日一句 人的一生 可以有所作为的时机只有一次 那就是现在 文章目录 前言 一 Day34习题 错题 解析1 总结 前言 一 Day34习题 错题 解析 1 解析
  • 升级 Linux 系统中的 Python 版本

    升级 Linux 系统中的 Python 版本 Python 是一种非常流行的编程语言 广泛应用于各种领域 包括 Web 开发 数据分析等 而对于 Linux 系统来说 Python 更是一个必须的组件 在系统运行和管理中都扮演了重要的角色
  • 大模型Founation Model

    一 背景 自从chatgpt gpt4以特别好的效果冲入人们的视野中 也使得AI产业发生了巨大变革 从17年以来的bert 将AI的各种领域都引入bert类的fine tune方法 来解决单个领域单个任务的一一个预训练模型 在学术界和工业界
  • 谈谈Linux epoll惊群问题的原因和解决方案

    近期排查了一个问题 epoll惊群的问题 起初我并不认为这是惊群导致 因为从现象上看 只是体现了CPU不均衡 一共fork了20个Server进程 在请求负载中等的时候 有三四个Server进程呈现出比较高的CPU利用率 其余的Server
  • 如何区分成员函数和构造函数?

    在面向对象编程中 成员函数和构造函数是类中定义的两种不同类型的函数 构造函数是一个特殊的成员函数 用于创建并初始化类的对象 构造函数的名称必须与类的名称相同 它没有返回值 并且在对象创建时自动调用 构造函数可以有参数 这些参数用于初始化类的
  • 字符串查找之 KMP 算法思路讲解和代码实现

    算法介绍 KMP 算法是一种改进的字符串匹配算法 由 D E Knuth J H Morris 和 V R Pratt 提出的 KMP 算法的核心是利用匹配失败后的信息 尽量减少模式串与主串的匹配次数以达到快速匹配的目的 KMP 算法的时间