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

KMP-next数组详解

KMP核心

核心是next数组,其本质上是基于模式串的可见性,操作模式串得出的

举一个简单例子:

序号/下标12345...
主串abab...
模式串ababc...
next[i]01123...

可以看到,在下标5的位置失配,肉眼可见,模式串应该从3的位置开始和主串的5位置开始比较

因为前面两个ab元素能够匹配上,无需再次验证

这便是next数组需要做的:提前标明哪个位置失配时,模式串指针应该从哪个位置开始和主串比较

整个过程,主串的指针是不回溯的,也就是不会返回,一直向前,这也是不同于朴素算法的关键

next数组

我们肉眼当然能很快知道失配时应该从哪个位置重新开始,但计算机不知道,所以需要设计next[]数组

next[i] 理解可以有以下两种:

  • 当模式串中第 i 位置(i=1除外)的字符失配时,模式串指针应该回到哪个位置继续和主串原失配位置比较
  • next[i]表示的值为:长度为i-1的子串的最长公共前后缀+1

   对于第二种理解:结合第一种理解,以"b"为例,b元素前的子串为"aa",可以知道,最长公共前后缀为1,当"b"失配时,指针指向的是2,也就是next[2] = 2;即跳过了最长公共前后缀的部分(1+1),因为那是已知的、最长的、无需再次匹配的有效子串

序号/下标12345678
模式串aabcaaaa
next[i]01211233

根据以上的例子,我们分别以两种方式来解释一下:

next[1]=0:特殊情况,固定值,模式串在第1个元素就失配,主串跟模式串的指针都要++

next[2]=1:特殊情况,固定值,模式串在第2个元素失配,模式串指针回到第一个位置重配

next[3]=2:在第3个元素失配,模式串指针回到第2个元素重配;子串"aa"的最长公共前后缀为1

next[4]=1:第4个元素失配,模式串指针回到第1个元素重配,子串"aab"最长公共前后缀为0

next[5]=1:第5个元素失配,模式串指针回到第1个元素重配,子串"aabc"最长公共前后缀为0

next[6]=2:第6个元素失配,模式串指针回到第2个元素重配,子串"aabca"最长公共前后缀为1

next[7]=3:第7个元素失配,模式串指针回到第3个元素重配,子串"aabcaa"最长公共前后缀为2

next[8]=3:第8个元素失配,模式串指针回到第3个元素重配,子串"aabcaaa"最长公共前后缀为2

可以看到,next[i]的值总是比它的子串的最长公共前后缀+1,印证了上面的理解

光说不练假把式,上面只是我们人眼推算出来的,代码应该如何实现

void kmp_next(Substr *substr, int *next)
{if(substr == NULL || next == NULL){return;}int i = 1;int j = 0;next[1] = 0;while(i < substr->len){if(j == 0 || substr->str[i] == substr->str[j]){i++;j++;next[i] = j;}else{j = next[j];}}
}

i 指向当前处理的元素,j 指针表示长度为 i 的子串的最长前后缀+1

分析代码:

代码的处理就是上面理解的具象化,所求的值就是:i+1 位置前的长度为 i 的子串的最长公共前后缀+1

代码中可以看到,先让i++,再将j++的值赋给next[i],求得:next[i+1] = j + 1

一个一个来看:

序号/下标12345678
模式串aabcaaaa
next[i]01211233

next[1]:特殊情况,next[1]固定为0;表示模式串第一个元素就和主串的i位置元素失配

next[2]:特殊情况,next[2]固定为1;分析:进入循环后判断:发现 j 为0,则子串"a"最长公共前后缀为0,i 和 j 同时++,next[2] = j = 1;

next[3]:i = 2;j = 1,发现str[1] == str[2],即子串"aa"最长公共前后缀为1,next[3] = j++ = 2;

next[4]:i = 3;j = 2;发现str[2] != str[3],即a != b,aa != ab,进入else分支,j = next[2]=1,再次循环判断,发现str[1] != str[3],即a != b,再次进入else分支,j = next[1] = 0;到这说明"aab"的最长公共前后缀为0;next[4] = j++ = 1;至于这个回退规则,相信花点时间能理解,我就不画蛇添足了

next[5]:i = 4;j = 1;说明"aab"最长公共前后缀为0,现在要求"aabc"的最长公共前后缀,str[1] != str[4],进入else分支,j = next[1] = 0;到这说明"aabc"的最长公共前后缀为0;next[5] = j++ = 1;

next[6]:i = 5;j = 1;说明"aabc"最长公共前后缀为0,现在要求"aabca"的最长公共前后缀,str[1] == str[5],进入if分支,即next[6] = 2;

next[7]:i = 6;j = 2;说明"aabca"最长公共前后缀为1,现在要求"aabcaa"的最长公共前后缀,str[2] == str[6],进入if分支,即next[7] = 3;

next[8]:i = 7;j = 3;说明"aabcaa"最长公共前后缀为2,现在要求"aabcaaa"的最长公共前后缀,str[3] != str[7],进入else分支,j = next[3] = 2;str[2] == str[7];进入if分支,即next[8] = j++ = 3

以上是以代码运行角度分析流程,有些复杂,如果表述有误,欢迎指正,希望对你能有帮助!

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

相关文章:

  • sigaction结构体详解
  • 推荐一款优质的开源博客与内容管理系统
  • 集团敏捷组织转型项目研讨材料(105页PPT)精品
  • Mac安装WebStorm for Mac v2025.1.1
  • PDF注释的加载和保存的实现
  • Enhancing Long Video Question Answering with Scene-Localized Frame Grouping
  • python中的推导式
  • Android PDFBox 的使用指南
  • 力扣热题100------136.只出现一次的数字
  • 【纵火犯的春天】纵火犯是如何解题leetcode的?
  • Python驱动的无人机多光谱-点云融合技术在生态三维建模与碳储量/生物量/LULC估算中的全流程实战
  • JDK9+ Method.class.getDeclaredFields() Method实例将不能再直接通过反射修改
  • 无人机航拍数据集|第4期 无人机太阳光伏板红外目标检测YOLO数据集10945张yolov11/yolov8/yolov5可训练
  • 大疆无人机使用eport连接Jetson主板实现目标检测
  • selenium操作指南
  • 前端路由守卫
  • JavaWeb服务器/servlet容器(Tomcat、Undertow 、WebLogic)
  • 前端应用场景题目(待总结优化)
  • 攻防世界WEB(新手模式)20-unseping
  • 基于 kubeadm 搭建 k8s 集群
  • 京东商品评论接口开发全指南:从数据获取到分析应用实战
  • 【20205CVPR-目标检测方向】基于事件的高效目标检测:具有空间和时间注意力的混合神经网络
  • Lodash 的终极进化Radashi
  • JAVA 程序员cursor 和idea 结合编程
  • 北京JAVA基础面试30天打卡03
  • SAP MR51 显示不是ALV格式的问题
  • 开源远程工具rustdesk
  • 力扣 hot100 Day67
  • Linux firewall 防火墙管理
  • SpringBoot 接入SSE实现消息实时推送的优点,原理以及实现