我对KMP算法的简单理解

news2024/11/17 8:18:24

我对KMP算法的简单理解

前言:字符串匹配问题

问题概述:

“字符串A是否为字符串B的子串?如果是,出现在B的什么位置?”这个问题就是字符串匹配问题。字符串A称为模式串(zs),字符串B称为主串(ss)。

其中,解决字符串匹配问题有两种方法:

  • 暴力求解(朴素模式匹配算法)
  • KMP算法

朴素模式匹配算法

思路

从主串的第一个字符开始与模式串的第一个字符进行比较,如果相同则模式串指针和主串的指针都往下走一位,再去进行比较。如果相同,则主串指针向下移动一位,模式串的指针回到初始的地方,重新开始进行比较。


#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
// 暴力求得子串
int re_index(string ss,string sz){
    int len_s = ss.length();
    int len_z = sz.length();
    int i = 0;
    int j = 0;
    while(i<len_s&&j<len_z){
        if(ss[i]==sz[j]){
            i++; j++;
        }else{
            i = i - j + 1; //i要从开始的地方+1再进行向后遍历
            j = 0;
        }
    }
    if(j==len_z){
        return i-j; // 回到i刚刚开始的地方
    }else{
        return -1;
    }
}
int main()
{
    string ss,sz;
    cin>>ss>>sz;
    cout<<re_index(ss,sz)<<endl;
    return 0;                   
}

KMP算法

KMP算法可以先读一下这两篇文章:

什么是KMP算法(详解)

可能是全网最清晰的KMP算法讲解

之后,KMP算法就可以分为两个部分:

K M P 算法 = 求 n e x t 数组 + 遍历求下标 KMP算法 = 求next数组 + 遍历求下标 KMP算法=next数组+遍历求下标

求next数组 == 求最长的公共前后缀

  • 公共前后缀

    一个字符串的前n个字符和后n个字符,分别是它的前缀和后缀,如果它的前缀和后缀相同,则它们的长度就是公共前后缀的长度。(⚠️:在这个情景中,单个字符的最长公共前后缀的长度为0,不是1)

  • next数组
    next[i]表示为:在前 $ i $ 位 ( 0 ~ i − 1 ) (0~i-1) 0i1的字符串中的最长公共前后缀的长度。

    • 比如: aba 的字符串中的最长公共前后缀的长度为 1 1 1({a})
    • 比如:abcab的最长公共前后缀的长度为 2 2 2 {ab}

    KMP算法的核心是:当某个位置匹配失败时,移动到这个位置之前的字符串的最长前缀的下一个字符继续匹配。而寻找这个位置的任务就交给next数组来完成

  • 求next数组
    将问题转化为:求最长的公共前后缀问题
    这个问题我感觉像是一个dp问题

    1. next[0] = -1 // 默认初始化为-1,因为当第一位置就匹配失败时,两个坐标都要向前进行+1操作
    2. next[1] = 0 // 单个字符的最长公共前后缀的长度为0
    3. next[i]的情况: 表示的是前i位的字符串中最长公共前后缀长度,也就是说
      1. next[i]中存储的数字是最长公共前缀的最后一个字符的下一个字符的坐标(在代码中一直用k来进行代替)
      2. sz[next[i]]等价于sz[k] 表示的是前i位的字符串中最长公共前缀串的下一个字符。而sz[i]恰恰表示的是该段子串的最后一个字符
        • sz[i]==sz[k] 也就是最长前缀的后一个字符与该段子串的最后一个字符相匹配时,就要将最长公共前后缀的长度去进行+1操作
        • 如果sz[i]!=sz[k] ,则该段子串的最长公共前后缀的长度仍然和之前长度相同,k不变。

// 求特定模式串(子串)的next数组
vector<int> re_next(string zs){
    int len = zs.length();
    vector<int> next(len,0);
    int k = -1;
    int i = 0;
    while(i<len){
        if(k==-1||zs[k]==zs[i]){
            next[i] = k;
            i++;k++;
        }else{
            next[i++] = k;
        }
    }
    return next;
}

求index坐标

求index的坐标基本就不难理解了

//求坐标
int re_index(string ss,string zs,vector<int> next){
    int len_s = ss.length();
    int len_z = zs.length();
    int i,j;
    i = 0;
    j = 0;
    while(j<len_z&&i<len_s){
        if(j==-1||ss[i]==zs[j]){
            i++;
            j++;
        }else{
            j = next[j];
        }
    }
    if(j==len_z){
        return i - j;
    }
    else{
        return -1;
    }
}

可以借助于视频来进行理解一下:

完整代码

/*---------------------------------------------------------------
              Proverb  : Make a little progress every day         
              AUthor   : programmerxing                               
              Question : 1                              
              Data     : 2023 - 01 - 29
              Time     : 23:21:22
----------------------------------------------------------------*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
// KMP算法
vector<int> re_next(string zs){
    int len = zs.length();
    vector<int> next(len,0);
    int k = -1;
    int i = 0;
    while(i<len){
        if(k==-1||zs[k]==zs[i]){
            next[i] = k;
            i++;k++;
        }else{
            next[i++] = k;
        }
    }
    return next;
}
int re_index(string ss,string zs,vector<int> next){
    int len_s = ss.length();
    int len_z = zs.length();
    int i,j;
    i = 0;
    j = 0;
    while(j<len_z&&i<len_s){
        if(j==-1||ss[i]==zs[j]){
            i++;
            j++;
        }else{
            j = next[j];
        }
    }
    if(j==len_z){
        return i - j;
    }
    else{
        return -1;
    }
}
int main()
{
    string zs,ss;
    cin>>ss>>zs;
    vector<int> next = re_next(zs);
    for(int e : next){
        cout<<e<<' ';
    }
    cout<<endl;
    cout<<re_index(ss,zs,next)<<endl;
    return 0;                   
}

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

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

相关文章

C++ 深入理解模板实现多态思想

文章目录前言一、模板与多态基础1.模板2.多态二、模板实现多态三、实际应用前言 对C/C学习感兴趣的可以看看这篇文章噢&#xff1a;C/C教程 最近有时间&#xff0c;便用WTL写了一个兼具群聊、单聊以及传输文件的聊天软件&#xff0c;过几天应该就能更新到 C/C教程系列 中了 …

EasyGBS+EasyNVS技术方案,如何实现对多现场国标视频平台的统一管理?

一、平台能力 1&#xff09;EasyGBS EasyGBS国标视频云服务平台支持无缝、完整接入内网或者公网的国标设备&#xff0c;在输出上&#xff0c;实现全平台、全终端输出。EasyGBS可将GB/T28181设备/平台推送的PS流转成ES流&#xff0c;并提供RTSP、RTMP、FLV、HLS、WebRTC等多种…

使用shell进行简单操作

目录 1、shell实现乘法表的打印 2、shell判定成绩等级 3、循环创建用户 1、shell实现乘法表的打印 要求&#xff1a;嵌套循环实现9*9乘法表&#xff08;两种方式&#xff09; 创建脚本文件&#xff1a;vim mcl.sh #!/bin/bash ######################### #File name:mcl.s…

idea插件及插件使用方法

CamelCase (下划线转驼峰) 使用快捷键&#xff1a;altshiftu。 按住altshift再不停的按U&#xff0c;会把选中内容的单词的下划线转驼峰转大写等&#xff0c;不停的转换。 Maven Helper Maven助手 安装之后再次打开pom文件&#xff0c;文件左下角会多出一个视图。 切换到"…

软件测试基础(三) 之 软件的生命周期

软件的生命周期一、软件的生命周期简述软件的生命周期中最早可能是客户&#xff0c;可能是产品的一个想法阶段&#xff0c;然后再到后来的一个需求阶段&#xff0c;再到开发人员去进行编码&#xff0c;去进行自己的自测&#xff0c;再提到软件测试人员进行综合测试&#xff0c;…

C语言之初识指针

前言 &#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏: &#x1f354;&#x1f35f;&#x1f32f; c语言初阶 &#x1f511;个人信条: &#x1f335;知行合一 &#x1f349;本篇简介:>:介绍c语言中的新知识—指针有关的知识. 金句分享: ✨知…

大数据舆情监控流程,TOOM大数据舆情监控范围

大数据舆情监控是一种通过大数据技术&#xff0c;分析社会舆情信息&#xff0c;掌握舆情动态的方法。它利用大数据的存储和处理能力&#xff0c;对海量的网络舆情数据进行收集、清洗、分析、呈现&#xff0c;帮助企业和机构了解公众的想法和评价&#xff0c;掌握舆情动态&#…

【正点原子FPGA连载】第三十一章Linux内核定时器实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Linux开发指南

1&#xff09;实验平台&#xff1a;正点原子MPSoC开发板 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id692450874670 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/thread-340252-1-1.html 第三十一章Linux…

【Linux】多线程详解(中)

&#x1f387;Linux&#xff1a; 博客主页&#xff1a;一起去看日落吗分享博主的在Linux中学习到的知识和遇到的问题博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a; 看似不起波澜的日复一日&#xff0c;一定会在某一天让你看见坚持…

哈希表的概念(散列表)

一、基本概念 散列表特点 &#xff1a; 数据元素的关键字与存储地址直接相关 通过哈希函数建立“关键字”与“存储地址”的联系 若不同的关键字通过散列函数映射到同一个值&#xff0c;则称它们为 “同义词” 通过散列函数确定的位置已经存放了其他元素&#xff0c;则称这种…

今日题目分享(两个维度的思考,双指针/动态规划,b站视频讲解)

先直接上连接 941有效的山脉数组 845数组中的最长山脉 2100适合打劫银行的日子. 2420找到所有好下标. 什么是两个维度&#xff1f; 这里是从代码随想录里面学习到的思考方式&#xff0c;开门见山地说&#xff0c;就是两个方面去考虑题目&#xff0c;比如&#xff0c;要求第…

vue前端框架应用案例(二)实现简单的SPA应用

目录路由使用步骤案例效果案例目录结构App.vueAbout.vueHome.vueindex.jsmain.jsindex.html本博客参考尚硅谷官方课程&#xff0c;详细请参考 【尚硅谷bilibili官方】 本博客以vue2作为学习目标&#xff08;请勿混淆v2与v3的代码规范&#xff0c;否则可能出现报错&#xff09…

docker搭建nacos集群

一、先搭建MySQL主从模式 Nacos使用delby作为内嵌数据库&#xff0c;在使用集群作为部署方式时&#xff0c;内嵌数据库无法保持数据同步与数据一致&#xff0c;故一般使用外接MySQL数据库的方式保存配置文件。使用一主一从的方式搭建&#xff0c;实现主从复制与读写分离。 1.…

线缆也可能是静电危害的罪魁祸首?

众所周知&#xff0c;几乎所有的电子元器件都是对静电敏感的&#xff0c;如果处理不当&#xff0c;将恶化元器件的性能&#xff0c;甚至造成彻底损坏。在低温干燥的环境中&#xff0c;极易产生静电&#xff0c;当然静电主要还是通过摩擦产生的。除了我们所熟知的静电产生的原因…

史上最全的测试用例设计方法

目录 前言 等价类划分方法&#xff1a; 边界值分析方法&#xff1a; 错误推测方法 因果图方法 判定表驱动分析方法 总结 前言 今天还是给大家带来一些干货&#xff0c;总结了一下测试用例的设计方法。具体内容太多我总结成了文档&#xff0c;获取方法在文末。这里截取部…

Swagger2Swagger3

一、什么是Swagger swagger是当下比较流行的实时接口文文档生成工具。接口文档是当前前后端分离项目中必不可少的工具&#xff0c;在前后端开发之前&#xff0c;后端要先出接口文档&#xff0c;前端根据接口文档来进行项目的开发&#xff0c;双方开发结束后在进行联调测试。 所…

Python程序设计之 —— 简易学生信息管理系统

大家好&#xff0c;我是 Enovo飞鱼&#xff0c;今天分享一个 Python程序设计之 —— 简易学生信息管理系统 &#xff0c;小白或者正在学习Python的小伙伴推荐阅读&#xff0c;加油&#x1f4aa;。 目录 前言 Python 简介 Python 特点 一、项目来源及背景 二、功能设计 …

PTA L1-023 输出GPLT(详解)

前言&#xff1a;本期是关于输出GPLT的详解&#xff0c;内容包括四大模块&#xff1a;题目&#xff0c;代码实现&#xff0c;大致思路&#xff0c;代码解读&#xff0c;今天你c了吗&#xff1f; 题目&#xff1a; 给定一个长度不超过10000的、仅由英文字母构成的字符串。请将字…

概论_第7章_参数估计__区间估计

先看知识结构图 一 置信区间 定义 定义&#xff1a; 设σ\sigmaσ 为总体的未知参数&#xff0c; θ^1θ^1(x1,x2,...,xn),θ^2θ^2(x1,x2,...,xn)\hat \theta_1 \hat\theta_1(x_1,x_2, ..., x_n), \hat \theta_2 \hat\theta_2(x_1,x_2, ..., x_n)θ^1​θ^1​(x1​,x2​,...,x…

YB菜菜的机器学习自学之路(七)——简单了解keras框架

YB菜菜的机器学习自学之路&#xff08;七&#xff09;——简单了解keras框架前提说明1. 机器学习框架-keras1.1 keras框架的特点1.2 keras框架实现一个神经元的建立的过程2. 举例说明2.1 一个神经元 和输入特征为1的案例2.2 多神经元 和单输入特征为1的案例2.3 多输入&#xff…