【数据结构与算法】字符串匹配,BF算法和KMP算法,next数组求法

news2024/11/19 2:43:02

朴素的模式匹配算法

bf算法
假设在主串S="helloworld"中找T="hellr"这个子串的位置

实现的思路如下

  • 第一轮:子串中的第一个字符和主串中的第一个字符进行比较
    • 如果相等,继续比较主串和子串中的第二个字符
    • 如果不相等,进行第二轮比较
  • 第二轮:子串中的第一个字符和主串中的第二个字符进行比较
    • 如果相等则继续比较子串和主串的下一个字符。
    • 如果不相等,这进行下一轮比较。
  • 第N轮:同第二轮
    如果主串中没有匹配的字符串就返回-1。
    在这里插入图片描述
 int bfFind(char*s,int slen,char*t,int tlen) {
     //主串和子串的指针,i主串,j子串
     int i, j;
     //主串比子串小,匹配失败,curLenght为串的长度
     if (slen< tlen)
         return -1;
     while (i < slen && j < tlen) {
         //对应字符相等,指针后移
         if (s[i] == t[j])
             i++, j++;
         else {  //对应字符不相等
             i = i -j + 1;   //主串指针移动
             j = 0; //子串从头开始
         }
         //返回子串在主串的位置
         if (j >= tlen) 
             return (i -tlen);
         else return -1;    
     }
 }

KMP算法

KMP算法是一种对朴素模式匹配算法的改进,核心就是利用匹配失败后的信息,尽量减少子主串的匹配次数,其体现就是 主串指针一直往后移动,子串指针回溯。
考虑这样的主串S="abcabadbca"和子串T=“abcabx”
如果采用朴素模式匹配算法的过程大致如下

  1. 第一个轮 主串S和子串T前五个字符匹配,第六个字符开始失配。
    在这里插入图片描述

  2. 第二轮 按照BF算法,接下来匹配主串第2个字符和子串的第一个字符
    在这里插入图片描述

  3. 第三轮
    在这里插入图片描述

由S[0]==T[0],S[1]==T[1],S[2]==T[2],且T[0],T[1],T[2]互不相等,那么第二和第三轮比较是可以跳过的。
4. 第四轮
在这里插入图片描述

  1. 第五轮
    在这里插入图片描述
    由于子串T[0]==T[3],T[1]==T[4],根据第一轮的比较,T[0…1]==S[3…4],那么第四、第五轮比较是可以跳过的。
  2. 第六轮
    在这里插入图片描述

那么,采用KMP算法在第一轮失配后会直接进行第六轮的比较。那么在某一个字符处失配后,子串指针要移动到那一个位置?KMP算法还要求一个next数组,next数组给出了要移动到的位置

next数组

next数组的公式
n e x t [ j ] = { − 1 当 j = 0 M a x ( k ∣ 1 < k < j 且 ′ p 0 ⋅ ⋅ ⋅ p k − 1 ′ = = ′ p j − k + 1 ⋅ ⋅ ⋅ p j − 1 ′ ) 0 其他情况 next[j]=\begin{cases} -1 & 当j=0 \\ Max(k|1<k<j且'p_0 ···p_{k-1}'=='p_{j-k+1}···p_{j-1}')\\ 0 & 其他情况\\ \end{cases} next[j]= 1Max(k∣1<k<jp0⋅⋅⋅pk1==pjk+1⋅⋅⋅pj1)0j=0其他情况

next数组的含义:next[j]表示T[j]字符前面的字符串中最大的公共前后缀的长度。

比如abcabx,这个字符串字符x前面的字符串abcab。

前缀集合:
{ a , a b , a b c , a b c a } \{ a,ab,abc,abca \} {a,ab,abc,abca}
后缀集合:
{ b , a b , c a b , b c a b } \{ b,ab,cab,bcab \} {b,ab,cab,bcab}

那么最长相等前后缀就是ab。

**前缀不包含最后一个字符,后缀不包含第一个字符

next数组的求法

请先点击这里看看求next数组的代码,如果看不懂请回到这里。

先看这个图,这个图表示的就是要查找的字符串,求next数组使用类似数学归纳法的三个步骤:

  1. 初始条件(next[0]=-1,k=-1,j=0;)
  2. 假设第j位和第j位前都求出来了
  3. 推论第j+1位
    在这里插入图片描述

这个图可以得到的条件如下:

next[j]==k
next[k]==绿色块的索引
next[绿色块的索引]=黄色块的索引
  1. next[j]==k这个条件,可以得到字符串A1==字符串A2
  2. next[k]==绿色块的索引,可以得到B1==B2
  3. next[绿色块的索引]=黄色块的索引,可以得到C1==C2
  4. 由1和2可以推出B1B2B3
  5. 由2和3可以推出C1C2C3,再结合4可以得到C1C2C3==C4

根据这些条件,接下来开始推导如果第j+1位失配时next[j+1]的值

已知A1==A2,那么A1和A2分别往后加一个字符是否会相等?

  1. 如果str[k]==str[j],显然A1和A2往后加一个字符会相等,这个时候next[j+1]的值就是k+1
  2. 如果str[k]!=str[j],那么str从0到j位的最大前后缀就不可能是A1和A2了,那么接下来从B1和B3这两个前后缀入手。

B1和B3分别往后加一个字符是否会相等?
处理B1和B3是先要挪一下k的位置next[k]==绿色块的索引,令k=next[k],也就是把k移到绿色块那里。

  1. 如果str[k]==str[j],显然B1和B2会往后加一个字符相等,这个时候next[j+1]的值就是k+1(next[j]+1)
  2. 如果str[k]!=str[j],那么str从0到j位的最大前后缀就不可能B1和B3了,那么接下来从C1和C4这两个前后缀入手······按照这个流程推导下去就可以把next[j+1]求出来了。(因为j+1位之前的next数组都已经假设求出来了,这个流程是一定会结束的)
    在这个过程要考虑一个特殊情况,当k=-1的时候是不能继续的(也就是根本就没有相同的前后缀),这个时候next[j+1]=0。比如abaca这个串求最后一个a的next值

求next数组代码

根据这个过程给出的求next数组的代码。

点击这里转到代码解释

void Getnext(char*str,int strlen,int*next){
   int j=0;
   int k=-1;
   next[0]=-1;
   while (j<strlen)
   {
        if(k==-1||str[j]==str[k]){
            ++j;
            ++k;
            next[j]=k;

        }else{
            k=next[k];
        }
   }
   
}

KMP算法的代码

int KMP(char*s,int slen,char*t,int tlen)
{
    
    int next[255];
    int i=0;
    int j=0;
    Getnext(t,tlen,next);
    while(i<slen&&j<tlen){
        if(j==-1||s[i]==t[j]){
            i++;
            j++;
        }else{
            j=next[j];
        }
    }
    if(j>=tlen){
        return i-tlen;
    }
    else{
        return -1;
    }
}
        j++;
    }else{
        j=next[j];
    }
}
if(j>=tlen){
    return i-tlen;
}
else{
    return -1;
}

}


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

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

相关文章

解决vue3 + vite + ts 中require失效的问题(require is not defind)

require is not defind因为require是属于webpack的方法&#xff0c;vite中找不到这个方法肯定报错 解决办法 通过vite官网了解到新的引入方式&#xff0c;我使用了其中一种 imgList: [{name: "lj",src: new URL(../../assets/img/applyList.png, import.meta.url).…

大数据 DataX 数据同步数据分析入门

目录 一、DataX 概览 1.1 DataX 是什么 1.2 DataX 3.0 概览 设计理念 当前使用现状 二、DataX 详解 2.1 DataX 3.0 框架设计 2.2 DataX 3.0 插件体系 2.3 DataX 3.0 核心架构 2.3.1 核心模块介绍 2.3.2 DataX 调度流程 2.4 DataX 3.0 的六大核心优势 2.4.1 可靠的…

为Mkdocs添加在线聊天(Tidio为例)

以Tidio为例,Tidio免费版已经完全够用且无需梯子 访问Tidio官网 要在您的网站上使用 javascript 代码方法安装 Tidio&#xff0c;您需要创建一个 Tidio 帐户。要创建 Tidio 帐户&#xff0c;请访问我们的网站<www.tidio.com>&#xff0c;然后单击 “开始” 按钮创建新的…

代码随想录算法训练营第23期day24|回溯算法理论基础、77. 组合

目录 一、回溯算法基础 回溯法模板 二、&#xff08;leetcode 77&#xff09;组合 剪枝 一、回溯算法基础 1.回溯的本质是穷举&#xff0c;穷举所有可能&#xff0c;然后选出想要的答案&#xff08;为了提升效率&#xff0c;最多再加一个剪枝&#xff09; 2.回溯法解决的…

第五章 运输层 | 计算机网络(谢希仁 第八版)

文章目录 第五章 运输层5.1 运输层协议概述5.1.1 进程之间的通信5.1.2 运输层的两个主要协议5.1.3 运输层的端口 5.2 用户数据报协议UDP5.2.1 UDP概述5.2.2 UDP的首部格式 5.3 传输控制协议TCP概述5.3.1 TCP最主要的特点5.3.2 TCP的连接 5.4 可靠传输的工作原理5.4.1 停止等待协…

SpringMVC源码分析(三)HandlerExceptionResolver启动和异常处理源码分析

问题&#xff1a;异常处理器在SpringMVC中是如何进行初始化以及使用的&#xff1f; Spring MVC提供处理异常的方式主要分为两种&#xff1a; 1、实现HandlerExceptionResolver方式&#xff08;HandlerExceptionResolver是一个接口&#xff0c;在SpringMVC有一些默认的实现也可以…

(ubuntu) 安装JDK

文章目录 前言参看java版本的命令&#xff1a;安装jdk命令安装jps关闭防火墙&#xff1a;查看端口占用&#xff1a;&#xff08;坑&#xff09;ubuntu上Mysql默认标明 区分大小写 前言 提示&#xff1a;常以为人是一个容器&#xff0c;盛着快乐&#xff0c;盛着悲哀。但是人不…

JUC并发编程——线程池学习:基础概念及三大方法、七大参数、四大拒绝策略(基于狂神说的学习笔记)

线程池 池化技术的本质&#xff1a;事先准备好一些资源&#xff0c;线程复用&#xff0c;用完即还&#xff0c;方便管理 默认大小&#xff1a;2 最大并发数max 根据电脑去设置&#xff0c;CPU密集型&#xff0c;IO密集型 线程池的好处&#xff1a; 降低资源的消耗提高响应的…

【马蹄集】—— 概率论专题

概率论专题 目录 MT2226 抽奖概率MT2227 饿饿&#xff01;饭饭&#xff01;MT2228 甜甜花的研究MT2229 赌石MT2230 square MT2226 抽奖概率 难度&#xff1a;黄金    时间限制&#xff1a;1秒    占用内存&#xff1a;128M 题目描述 小码哥正在进行抽奖&#xff0c;箱子里有…

双目项目实战---测距(获取三维坐标和深度信息)

目录 1.简介 2.模块讲解 2.1 立体校正 2.1.1 校正目的 2.1.2 校正方法 2.2 立体匹配和视差计算 2.3 深度计算 3.完整代码 1.简介 双目视觉是一种通过两个摄像机&#xff08;或者两个镜头&#xff09;同时拍摄到同一个场景&#xff0c;再通过计算机算法来获取该场景深度…

C++ - 智能指针的定制删除器

前言 在上一篇C 文章当中&#xff0c;对 智能指针的使用&#xff0c;做了很详细的介绍&#xff0c;对 C11 和 C98 库当中实现的一些常用智能指针做了很详细的介绍&#xff0c;但是智能指针的使用还有一些拓展用法。上篇文章链接&#xff1a;C - 智能指针 - auto_ptr - unique_…

Stm32_标准库_16_串口蓝牙模块_手机与蓝牙模块通信_手机传入信息能对芯片时间日期进行更改

实现了手机发送信息给蓝牙模块&#xff0c;程序对数据进行分析拆解&#xff0c;并更新自身数据 main.c: #include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "Serial.h" #include "Ti…

无人驾驶路径规划(一)全局路径规划 - RRT算法原理及实现

前言&#xff1a;由于后续可能要做一些无人驾驶相关的项目和实验&#xff0c;所以这段时间学习一些路径规划算法并自己编写了matlab程序进行仿真。开启这个系列是对自己学习内容的一个总结&#xff0c;也希望能够和优秀的前辈们多学习经验。 一、无人驾驶路径规划 众所周知&a…

Google Authenticator 和gitlab使用的方法配置Google AuthenticatorGoogle

Google Authenticator 和gitlab使用的方法 目录概述需求&#xff1a; 设计思路实现思路分析1.配置过程&#xff1a; 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,make a bette…

D201126 D201129 支持以太网高级物理层(APL)

D201126 D201129 支持以太网高级物理层(APL) 全球技术和软件领导者艾默生宣布了基于其无限自动化愿景&#xff0c;并作为其下一代以软件为中心的工业自动化架构的基础。新技术的发布将超越传统的控制系统&#xff0c;创建一个更先进的自动化平台&#xff0c;为人类和塑造世界…

【网络】网络层协议:IP(待更新)

文章目录 IP 协议1. 基本概念2. IP 报头解析 &#x1f53a;IP 的 网段划分&#xff1a; IP 协议 IP 不是面向字节流的&#xff0c;而是发送接收一个个的数据包 1. 基本概念 主机&#xff1a;配有 IP 地址的设备 路由器&#xff1a;配有单个或多个 IP 地址&#xff0c;且能进行…

【1314. 矩阵区域和】

目录 一、题目描述二、算法思想三、代码实现 一、题目描述 二、算法思想 三、代码实现 class Solution { public:vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k) {//先预处理数组int nmat.size();//行int mmat[0].size();…

flutter开发实战-下拉刷新与上拉加载更多实现

flutter开发实战-下拉刷新与上拉加载更多实现 在开发中经常遇到列表需要下拉刷新与上拉加载更多&#xff0c;这里使用EasyRefresh&#xff0c;版本是3.3.21 一、什么是EasyRefresh EasyRefresh可以在Flutter应用程序上轻松实现下拉刷新和上拉加载。它几乎支持所有Flutter Sc…

C++指针和引用

1、引用必须初始化&#xff0c;指针不必&#xff0c;所以说引用使你更安全的指针&#xff1b; 2、在汇编代码&#xff0c;指针和引用一模一样&#xff1b; 3、引用只有一级引用&#xff0c;没有多级引用&#xff1b; 4、引用必须引用一个能取地址的变量&#xff1b; 左值&…

第三章 内存管理 五、动态分区分配算法(首次适应算法、最佳适应算法、最坏适应算法、临近适应算法)

目录 一、首次适应算法 1、算法思想&#xff1a; 2、如何实现&#xff1a; 3、两种常用的数据结构: &#xff08;1&#xff09;空闲分区表、空闲分区链 4、例子 二、最佳适应算法 1、算法思想: 2、如何实现: 3、例子&#xff1a; 三、最坏适应算法 1、算法思想&…