KMP算法 看这一篇就够了 图解刨析+代码

news2024/11/18 13:32:26

目录

问题背景

逐步剖析 KMP如何优化暴力做法

思考

公共前后缀

next数组

如何构建next数组:

代码实现


问题背景

给定一个字符串 S,以及一个模式串P, P 在字符串 S 中多次作为子串出现。

求出模式串 P 在字符串 S 中所有出现的位置的起始下标。

逐步剖析 KMP如何优化暴力做法

暴力做法为双重for循环一一比对,时间复杂度为O(len_s*len_p)。

暴力枚举
for(int i=0;i<s_len;i++){
    for(int j=0;j<p_len;j++){
        if(s[i+j]==p[j])
            j++
        if(j==p_len){
            //匹配成功逻辑
        };   
    }
}

思考

那么某一个位置出现匹配失败的时候,我们能否利用已知信息来免去不必要的比对?

如图当指针分别指向 i,j处时,黑色框中的部分是完全一致的,能否挖掘这条信息的价值来提高效率?

答案时肯定的:KMP三位大佬(D.E.Knuth,J.H.Morris,V.R.Pratt)通过研究,找到了可以利用已知信息来优化暴力算法的方式,并引入了"公共前后缀"的概念。

公共前后缀

此时此时黄色和红色都是匹配失败时s,p相同部分的共同前后缀,而黄色是"最长公共前后缀"。 

而KMP三位学制通过研究发现 当在某个位置出现匹配失败时,只需让指向p的指针回退到相同部分的"最长公共前缀"的后一个位置,继续比对即可,不需要回退指向s的指针。

因此最长公共前后缀不能刚好是匹配失败时p,s的相同部分,不然j=j,陷入死循环。

 若还是匹配不上即继续回退j指针,若j回退到开头并且依旧匹配失败,则直接让i++。

伪代码:

    int i=0,j=0;
    while(i<len_s){
        if(s_arr[i]==p_arr[j]) i++,j++;
        else{
            if(j!=0) 回退j至公共前缀后;
            else i++;
            continue;
        }
        if(j==len_p){
            //匹配成功逻辑
        }
    }

现在关键点就变成了如何高效地求得最长公共前后缀的长度呢?

next数组

这里就要引入另一个关键概念:"next数组"

我们可以通过预先遍历一遍p字符串构建一个名为next的数组,next[i]的值就代表:区间[p[0],p[i]]的最长公共前后缀长度,匹配失败时让j=next[j-1]即可。

如何构建next数组:

思路:

        1.设定初始值next[0]=0(最长前后缀不能是子串本身) ,指针 j从1开始遍历p字符串。

        2.如果[p[0]~p[j-1]]的最长前后缀长度为len,此时若p[len]==p[j]那么[p[0]~p[j]]的最长前后缀长度就为len+1,next[j]=len+1;

        3.若p[len]!=p[j],我们就要尝试更短的前后缀

 

此时len为4的行不通,要尝试更短的"bab"或"ab"相当于是要找后缀的后缀与前缀的前缀的最长共同部分,因为前后缀相同,所以等价于找前缀的最长共同前后缀:len=next[len-1]。

代码实现

#include <iostream>
using namespace std;
const int N = 1e7 + 10;
// KMP 算法
// 核心思想:
// 通过每次匹配失败时已经掌握的信息来跳过暴力做法中"必定失败"和"一定成功"的一一比对
char p_arr[N], s_arr[N];
int len_p, len_s;
int next_arr[N];
//
// 构建next数组
void next_arr_init()
{
    int len = 0;
    next_arr[len] = 0;
    // i 从1开始
    int i = 1;
    // 遍历
    while (i < len_p)
    {
        // 相等++
        if (p_arr[i] == p_arr[len])
            next_arr[i++] = ++len;
        // 不相等
        else
            // len 已经为0
            if (len == 0)
                next_arr[i++] = 0;
            // len不为0
            else
                len = next_arr[len - 1];
    }
}
int main()
{
    cin >> len_p >> p_arr >> len_s >> s_arr;
    next_arr_init();
    int i = 0, j = 0;
    // 遍历 s
    while (i < len_s)
    {
        // 相等 更新 i,j
        if (s_arr[i] == p_arr[j])
            i++, j++;
        // 不相等
        else
        {
            if (j) // j不在开头 即查表
                j = next_arr[j - 1];
            else // 在开头 则这个位置匹配失败 更新 i
                i++;
            continue;
        }
        // 匹配成功
        if (j == len_p)
        {
            cout << i - len_p << ' ';
            // 更新j寻找下一个子串
            j = next_arr[j - 1];
        }
    }
    return 0;
}

KMP创始人之一  Donald Ervin Knuth. 

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

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

相关文章

说话人识别中的Temporal pooling(时序池化)

概述 Temporal pooling&#xff08;时序池化&#xff09;是说话人识别神经网络中&#xff0c;声学特征经过frame-level变换之后&#xff0c;紧接着会进入的一个layer。目的是将维度为(bs,F,T)(bs,F,T)(bs,F,T)的特征图&#xff0c;变换成维度为(bs,F)(bs,F)(bs,F)的特征向量 …

再不来看看常用的PyCharm快捷键就out了,玩转PyCharm仅此一篇!

最近在学习Python的数据可视化项目&#xff0c;在大学有学过Python&#xff0c;还有一些基础的。目前虽说已经工作&#xff0c;但是兴趣使然&#xff0c;依然想在空闲时间学一些其他技能来充实自己&#xff0c;未雨绸缪&#xff01; 在使用工具的时候&#xff0c;必定会对工具有…

【话题:工作生活】2021年工作总结--这些人,那些事。

Hello Everyone&#xff0c; 我又开始撰写自己的工作总结了。2021年的工作总结&#xff0c;拖得太久&#xff0c;拖得我也不想写了。每次写自己一年的工作总结&#xff0c;总是要耗费我大量的时间与心力&#xff0c;有时&#xff0c;我也真的是心好累。 好了&#xff0c;懒散、…

【人工智能原理自学】隐藏层:神经网络为什么Working

&#x1f60a;你好&#xff0c;我是小航&#xff0c;一个正在变秃、变强的文艺倾年。 &#x1f514;笔记来自B站UP主Ele实验室的《小白也能听懂的人工智能原理》。 &#x1f514;本文讲解隐藏层&#xff1a;神经网络为什么Working&#xff0c;一起卷起来叭&#xff01; 目录一、…

深度学习 11 梯度下降算法改进

数据初始化要点: 1. 梯度下降算法的三种方式: 批量梯度下降法(batch)&#xff0c;即同时处理整个训练集.小批量梯度下降法&#xff08;Mini-Batch &#xff09;每次同时处理固定大小的数据集.随机梯度下降法&#xff08;stochastic gradient descent&#xff09;, 每次随机选…

acwing基础课——约数

由数据范围反推算法复杂度以及算法内容 - AcWing 常用代码模板4——数学知识 - AcWing 基本思想&#xff1a; 首先&#xff0c;约数&#xff0c;又称因数。整数a除以整数b(b≠0)除得的商正好是整数而没有余数&#xff0c;我们就说a能被b整除&#xff0c;或b能整除a。a称为b的…

怎样才能过好这一生?

文章目录1. 日拱一卒&#xff0c;功不唐捐1.1 适当的时候给自己一个奖励1.2 一个人可能走的更快&#xff0c;但一群人才能走的更远1.3 通过一些事情去逼自己一把1.4 从真理中去感悟1.5 当你面临绝路时2. 梦想的意义不在于实现3. 孤独4. 烦恼5. 别总说来日方长6. 忍和韧性7. 事情…

【linux kernel】linux内核重要函数 | do_initcalls

文章目录一、导读二、do_initcalls三、构造section并添加函数&#xff08;3-1&#xff09;构造初始化调用section&#xff08;3-2&#xff09;向section中添加函数四、总结一、导读 在linux内核启动过程中&#xff0c;会向终端打印出很多的日志信息&#xff0c;从这些日志信息…

c++开源协程库libgo介绍及使用

协程这个概念&#xff0c;最近这几年可是相当地流行了。尤其 go 语言问世之后&#xff0c;内置的协程特性&#xff0c;完全屏蔽了操作系统线程的复杂细节。甚至使 go 开发者“只知有协程&#xff0c;不知有线程”了。当然 C也有高性能的协程库&#xff0c;比如我了解到的微信的…

基于微信小程序的企业职工薪资查询系统小程序

文末联系获取源码 开发语言&#xff1a;Java 框架&#xff1a;ssm JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7/8.0 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9 浏览器…

卷积神经网络(CNN)详细介绍及其原理详解

文章目录前言一、什么是卷积神经网络二、输入层三、卷积层四、池化层五、全连接层六、输出层七、回顾整个过程总结前言 本文总结了关于卷积神经网络&#xff08;CNN&#xff09;的一些基础的概念&#xff0c;并且对于其中的细节进行了详细的原理讲解&#xff0c;通过此文可以十…

自动(智能)驾驶 | 4D雷达的数据集

上篇文章分享了关于Oculii 4D雷达的两篇报告。数据集是一个非常重要的研究工具&#xff0c;对于4D雷达领域来说&#xff0c;处于一个研究前沿的位置&#xff0c;鲜有公开的数据集&#xff0c;目前能找到的数据集有&#xff1a; 这些文章中的数据集有不少博主也写过&#xff0c…

S1000D规范导读

S1000D最初是由欧洲航空工业联盟开发的技术出版物规范&#xff0c;它主要为具有较长生命的复杂产品运行和维修而设计。这些年不断发展&#xff0c;已经扩展到这些行业的产品&#xff1a;国防系统 - 包括海、陆、空的产品&#xff0c;民用航空产品&#xff0c;基建行业产品和船舶…

15/365 java static final

1.static属性,方法 类内属性或方法用static修饰&#xff0c;表示该属性或方法属于类&#xff0c;不依赖于实例对象&#xff0c;所以不需要用对象调用&#xff0c;而是直接用类名调用。 static方法只能调用其他static方法&#xff0c;而普通方法可以调用其他的普通方法和stati…

Vue3商店后台管理系统设计文稿篇(二)

记录使用vscode构建Vue3商店后台管理系统&#xff0c;这是第二篇&#xff0c;主要记录Vue3中生命周期钩子&#xff0c;模板语法&#xff0c;以及相关的代码 文章目录一、Vue3生命周期二、Vue3模板语法三、代码展示正文内容&#xff1a; 一、Vue3生命周期 每个 Vue 实例在被创建…

拆机详解2:比Macintosh还早?苹果Lisa拆解

hello大家好&#xff0c;我是每天&#xff08;实际并不是每天&#xff0c;你们点的赞太少了&#xff0c;每人点一个赞我就日更&#xff09;给你们讲解的Eric_Bells.这里感谢博主半身风雪的支持&#xff0c;我会更新的&#xff01;看到的麻烦点个关注谢谢拉 今天唠唠一台比Maci…

【蓝桥杯基础题】2017年省赛—九宫幻方

&#x1f451;专栏内容&#xff1a;&#x1f449;蓝桥杯刷题&#x1f448;⛪个人主页&#xff1a;&#x1f449;子夜的星的主页&#x1f448;&#x1f495;座右铭&#xff1a;前路未远&#xff0c;步履不停 目录一、题目背景二、题目描述1.问题描述2.输入格式3.输出格式4.一个例…

CAN通信----(创芯科技)CAN分析仪使用----CANTest安装和驱动安装

前言 我在调试CAN通信时&#xff0c;使用的是在淘宝买的CAN分析仪。 CAN分析仪的实物如下&#xff1a; 使用CAN分析仪&#xff0c;调试CAN通信&#xff0c;PC电脑端需要使用CANTest测试软件&#xff0c;还需要安装驱动。 一、创芯科技 CAN分析仪资料包下载 步骤1&#xff1…

测开-基础篇

一、软件测试的生命周期 先来回顾软件的生命周期 &#x1f351;软件的生命周期 需求分析--》计划--》设计--》编码--》测试--》运营维护 需求分析&#xff1a;进行市场分析&#xff0c;这个需求量大不大&#xff1f;投入与盈利的占比&#xff1f;技术上 能否实现或者说实现的…

深度学习 10 神经网络简介

1. 深度学习和机器学习的主要区别在于对数据的处理, 机器学习主要通过算法直接进行推断, 而深度学习主要通过神经网络对各种算法进行加权, 然后汇总得出结论. 2. 常用的激活函数: tanh函数relu函数leaky relu函数1.1 深度学习介绍 1.1.1 区别 机器学习的特征工程步骤是要靠手…