当前位置: 首页 > news >正文

KMP算法详解 -- 串的模式匹配

        学数据结构时, 参考课本无法让我理解模式匹配的 KMP 算法, 在和AI探讨之后遂有所领悟 , 把我的理解与心得写在下面.

  • KMP算法 -- next数组超详细解析

    一句话概括: 把next数组理解为"当在这里失败时,我知道前面有一段是重复的,所以可以直接跳到重复段开始的地方继续匹配"

    • 1. 为什么需要next数组?

      • 暴力匹配的问题:

        • // 暴力匹配示例
          主串:   a b a b a b c a
          模式串: a b a b c
          ↑ ↑ ↑ ↑ ×

        • 当匹配到第5个字符时失败,暴力算法会:

          • 主串回退到第2个字符'b'

          • 模式串回退到开头

          • 重新开始匹配

        • 问题:主串指针回退,效率低!

    • 2. next数组的核心思想

      核心洞察:匹配失败时,利用已经匹配成功的信息,让模式串"智能跳转",主串指针永不回退!

    • 3. next数组到底是什么?

      • 3.1 基本定义

        • 对于模式串中的每个位置j,next[j]表示:

          当模式串第j个字符匹配失败时,应该跳转到模式串的哪个位置继续匹配

      • 3.2 更直观的理解

        • next[j] = 模式串前j个字符组成的子串中,最长相等前后缀的长度
        • 什么是前后缀?

          • 前缀:包含首字符但不包含尾字符的所有连续子串

          • 后缀:包含尾字符但不包含首字符的所有连续子串

    • 4 .手动计算next数组(重点)!

      • 让我们用模式串"ababc"来一步步计算:
      • 步骤1:列出所有子串并找出最长相等前后缀

        • 位置j子串(0到j-1)所有前缀所有后缀最长相等前后缀next[j]
          0(空串)-1
          1"a"0
          2"ab""a""b"0
          3"aba""a","ab""a","ba""a" (长度1)1
          4"abab""a","ab","aba""b","ab","bab""ab" (长度2)2
        • 所以next = [-1, 0, 0, 1, 2]
      • 步骤2:验证理解

        • 让我们手动模拟匹配过程:
        • 主串:     a b a b a b c a
          模式串: a b a b c
          0 1 2 3 4      ← 位置索引
          next:     -1 0 0 1 2
        • 位置0-3匹配成功,位置4失败(c≠a)
          此时j=4, next[4]=2
          模式串跳转到位置2继续匹配:
          a b a b c
          a b a b c  ← 跳转后
        • 为什么能这样跳转?
        • 因为前4个字符"abab"中,最长相等前后缀是"ab",所以我们可以直接把前缀"ab"对齐到后缀"ab"的位置!
    • 5 算法构建next数组的原理

      • 5.1 核心观察

        • 如果我们已经知道next[j] = k,那么:

          • 如果pattern[j] == pattern[k],则next[j+1] = k + 1

          • 如果不等,就找更短的相等前后缀:k = next[k]

      • 5.2 详细推导过程

        • 还是以"ababc"为例:
        • 初始化: next[0] = -1, j=0, k=-1

          循环开始:
          1. j=0, k=-1 → k==-1 → j=1, k=0, next[1]=0
          解释:位置1前面只有"a",没有相等前后缀

          2. j=1, k=0 → pattern[1]='b' ≠ pattern[0]='a' 
          → k = next[0] = -1
          → k==-1 → j=2, k=0, next[2]=0
          解释:位置2前面是"ab",没有相等前后缀

          3. j=2, k=0 → pattern[2]='a' == pattern[0]='a'
          → j=3, k=1, next[3]=1
          解释:位置3前面是"aba",最长相等前后缀是"a"(长度1)

          4. j=3, k=1 → pattern[3]='b' == pattern[1]='b'
          → j=4, k=2, next[4]=2
          解释:位置4前面是"abab",最长相等前后缀是"ab"(长度2)

      • 5.3 可视化理解

        • 前后缀匹配的直观展示

          • 对于模式串"ababc"
          • 位置3的分析:
            子串: a b a
            前缀: a, ab
            后缀: a, ba
            相等前后缀: "a" ← 所以next[3]=1

            位置4的分析:  
            子串: a b a b
            前缀: a, ab, aba
            后缀: b, ab, bab  
            相等前后缀: "ab" ← 所以next[4]=2

        • 跳转的几何意义

          • 匹配失败时:
            主串:   ... a b a b ? ...
            模式串:   a b a b c
            ↑ 失败位置j=4

            查看next[4]=2,意味着前4个字符"abab"中:
            前缀"ab" = 后缀"ab"

            所以我们可以把模式串移动,让前缀对齐刚才匹配的后缀:
            主串:   ... a b a b ? ...
            模式串:          a b a b c
            ↑   从位置2继续匹配

      • 5.4 next数组的本质

        • 是模式串的"自相似性"描述

        • 记录每个位置前面子串的最长相等前后缀长度

        • 指导匹配失败时的智能跳转

    • 6. OJ例题及详解 (Java语言实现)

      • 题目描述

        • 学习KMP算法,给出主串和模式串,求模式串在主串的位置

          算法框架参考课本第四章

          输入

          第一个输入t,表示有t个实例

          第二行输入第1个实例的主串,第三行输入第1个实例的模式串

          以此类推

          输出

          第一行输出第1个实例的模式串的next值

          第二行输出第1个实例的匹配位置,位置从1开始计算,如果匹配成功输出位置,匹配失败输出0

          以此类推

          IO模式

          本题IO模式为标准输入/输出(Standard IO),你需要从标准输入流中读入数据,并将答案输出至标准输出流中。

          输入样例                                                                                                                                                                                                                            3
          qwertyuiop
          tyu
          aabbccdd
          ccc
          aaaabababac
          abac                                                                                                                                                                                                                                输出样例                                                                                                    -1 0 0 
          5
          -1 0 1 
          0
          -1 0 0 1 
          8
      • 代码及详解
        • import java.util.Scanner;/*** KMP算法Java实现* 用于解决字符串匹配问题*/
          public class Main {/*** 构建KMP算法的next数组* @param pattern 模式串* @return next数组* * next数组原理:* - next[j]表示当模式串中第j个字符与主串不匹配时,*   应该跳转到模式串的哪个位置继续比较* - next[0]固定为-1,表示模式串需要从头开始匹配*/public static int[] buildNext(String pattern) {int m = pattern.length();int[] next = new int[m];next[0] = -1;  // 第一个字符的next值固定为-1int j = 0;      // 主指针,遍历模式串int k = -1;     // 前缀指针,记录最长公共前后缀长度// 构建next数组的核心循环while (j < m - 1) {// 情况1:k为-1,表示没有公共前后缀,从头开始// 情况2:当前字符匹配,找到更长的公共前后缀if (k == -1 || pattern.charAt(j) == pattern.charAt(k)) {j++;k++;next[j] = k;  // 记录当前位置的next值} else {// 字符不匹配时,利用已计算的next值进行回溯k = next[k];}}return next;}/*** KMP字符串匹配算法* @param text 主串* @param pattern 模式串* @return 匹配位置(从1开始),未找到返回0* * 算法优势:* - 主串指针不回溯,提高匹配效率* - 时间复杂度O(n+m),其中n为主串长度,m为模式串长度*/public static int kmpSearch(String text, String pattern) {// 边界情况处理if (pattern.isEmpty()) return 1;  // 空模式串默认匹配位置1if (pattern.length() > text.length()) return 0;  // 模式串比主串长int n = text.length();int m = pattern.length();int[] next = buildNext(pattern);int i = 0;  // 主串指针,永不回溯int j = 0;  // 模式串指针// 匹配过程while (i < n && j < m) {// j == -1:模式串需要从头开始匹配// 当前字符匹配成功:继续比较下一个字符if (j == -1 || text.charAt(i) == pattern.charAt(j)) {i++;j++;} else {// 不匹配时,模式串指针根据next数组智能跳转j = next[j];}}// 判断匹配结果if (j == m) {// 匹配成功,返回位置(从1开始计算)return i - j + 1;} else {// 匹配失败return 0;}}/*** 主函数:处理输入输出*/public static void main(String[] args) {Scanner scanner = new Scanner(System.in);// 读取测试用例数量int t = scanner.nextInt();scanner.nextLine();  // 消耗换行符// 处理每个测试用例for (int i = 0; i < t; i++) {// 读取主串和模式串String text = scanner.nextLine();String pattern = scanner.nextLine();// 构建next数组int[] next = buildNext(pattern);// 输出next数组for (int j = 0; j < next.length; j++) {System.out.print(next[j] + " ");}System.out.println();// 执行KMP匹配并输出结果int pos = kmpSearch(text, pattern);System.out.println(pos);}scanner.close();}
          }

http://www.dtcms.com/a/516484.html

相关文章:

  • 用php做网站的方法学网站建设前途
  • 网站不用下载免费软件曰本孕妇做爰网站
  • 【微信小程序 + 消息订阅 + 授权】 微信小程序实现消息订阅流程介绍,代码示例(仅前端)
  • 网站开发找哪家什么查网站是否降权
  • 【经典书籍】C++ Primer 第13类继承精华讲解
  • “VMware与vmx86驱动程序版本不匹配:预期为:417,实际为416。”解决步骤,亲测有效!!!
  • 查找组成一个偶数最接近的两个素数
  • 获取文件版本(C++源码)
  • 济南网站建设鲁icp备附近展览制作工厂
  • 在Windows WSL2中安装Ubuntu和Docker的完整指南
  • Ubuntu 22 .04安装CUDA, cuDNN, TensorRT
  • Linux编辑神器——vim工具的使用
  • UPS-不间断电源系统
  • AMDGPU/KFD IV(Interrupt Vector)信息结构及实现
  • 网站开发公司计划书如何做英文网站的外链
  • 彬县网站建设it外包前景
  • 网站集约化做暧暧国外网站
  • 基于python的电子商务管理系统
  • Git Remote 实现双向仓库同步教程(适合跨公司协作)
  • 检测网站开发语言工具wordpress免回复
  • 【python】Pillow 快速入门
  • [特殊字符] [特殊字符][特殊字符]这样的 ​Emoji 表情符号​ 是怎么来的、怎么显示出来的,以及它们到底是怎么工作的
  • Salesforce Connected App 创建指南
  • 做的比较好的国外网站一级页面布局分析海外短视频服务器
  • 42.接雨水
  • 衡水网站建设地方网页界面设计案例分析
  • Process Monitor 学习笔记(5.24):工具栏参考与高效快捷键指南
  • UE5 材质-15:车漆-不透明-透明图层,FBX格式的介绍,如何导入外部模型FBX汽车,下载与使用官方的汽车材质 automotive materials,
  • qt实用学习案例:数据库设计+图表显示+model-view模式+样式表定制
  • 脉冲神经网络最新文献合集-XX