图解+原理推导完全读懂KPM算法

article/2025/10/9 7:50:11

文章目录

  • 串定长顺序存储方式
  • 串的模式匹配
    • BF算法
    • KMP算法
      • KMP算法原理
      • KMP算法实现
      • 求模式串T的next值算法
    • 时间复杂度分析
      • BF算法分析
      • KMP算法分析
      • KMP算法与BF算法比较

串定长顺序存储方式

我们显式地在串的索引为0处存储串长。

#define MAXSTRLEN 255   // 用户可在255以内定义最大串长
typedef unsigned char SString[MAXSTRLEN+1]; //0号单元存放串的长度

串的模式匹配

模式匹配:设有主串S和子串T,子串在主串中的定位称为模式匹配或串匹配(字符串匹配) 。
通常也把主串S称为目标串,把子串T称为模式串。
模式匹配成功是指在目标串S中找到一个模式串T是S的子串,返回T在S中的位置。
模式匹配不成功则指目标串S中不存在模式串T不是S的子串,返回-1。

BF算法

又称简单的、朴素的、穷举的模式匹配算法。算法的思路是
将主串的第 i 个字符和模式的第一个字符比较,

  1. 若相等,继续逐个比较后续字符;直到主串的一个连续子串字符序列与模式相等。返回值为主串中模式串匹配的子序列第一个字符的序号,即匹配成功。
  2. 若不等,从主串的下一字符起,重新与模式的第一个字符比较。
int Index_BF( SString S, SString T )
{// 返回模式串T在主串S中的位置。若不存在,则返回0。T非空int i = 1,j = 1;while ( i <= S[0] && j <= T[0] ) {if ( S[i] == T[j] ) {++i;++j;} // 继续比较后续字符else {i = i - j + 2;j = 1;} // 指针回溯重新开始下一趟匹配}if(j>T[0]) return i-T[0]; //返回匹配成功时主串对应的第一个字符的下标else return 0; //模式匹配不成功
} // Index_BF

时间复杂度:若主串s长度为n,模式串t长度为m
最好情况下的时间复杂度为O(m)。
算法在字符比较不相等,需要回溯(即i=i-j+1):即退到s中的下一个字符开始进行继续匹配。
最坏情况下每趟比较m次,进行 n - m + 1 n-m+1 nm1趟匹配,总比较次数为 m ( n − m + 1 ) m(n-m+1) m(nm+1),时间复杂度为 O ( n × m ) O(n×m) O(n×m)

KMP算法

KMP算法原理

KMP算法一种改进的模式匹配算法,是D.E.Knuth、V.R.Pratt、J.H.Morris于1977年联合发表,KMP算法又称克努特-莫里斯-普拉特操作。KMP算法的设计思想:当出现不匹配时,利用已经比较过的已知内容,来避免将主串指针回退到已比较的字符之前。

实现KMP算法需要解决的问题:

  1. 为什么主串的“指针”:i可以不回溯?
    若某趟在 S i S_i Si T j T_j Tj“失配”时,
    T 1 ∼ T j − 1 = S i − j + 1 ∼ S i − 1 T _{1} \sim T _{ j -1}= S _{ i - j +1} \sim S _{ i -1} T1Tj1=Sij+1Si1
    主串的切片 S i − j + 1 ∼ S i − 1 S _{ i - j +1} \sim S _{ i -1} Sij+1Si1的后缀中是否有能成功匹配的前缀,仅和 T 1 ∼ T j − 1 T _{1} \sim T _{ j -1} T1Tj1的性质有关,可以在匹配算法之前预计算出来。

  2. 若某趟在 S i S_i Si T j T_j Tj“失配”时,主串中i不回溯,下一步进行 S i S_i Si T k T_k Tk的比较,那么k如何求出?
    S i − k + 1 ∼ S i − 1 S _{ i - k +1} \sim S _{ i -1} Sik+1Si1的后缀必须是T的前缀:
    T 1 ∼ T k − 1 = S i − k + 1 ∼ S i − 1 T _{1} \sim T _{ k -1}= S _{ i - k +1} \sim S _{ i -1} T1Tk1=Sik+1Si1
    S i − k + 1 ∼ S i − 1 S _{ i - k +1} \sim S _{ i -1} Sik+1Si1在上一步已经与T匹配的部分:
    T j − k + 1 ∼ T j − 1 = S i − k + 1 ∼ S i − 1 T _{ j - k +1} \sim T _{ j -1}= S _{ i - k +1} \sim S _{ i -1} Tjk+1Tj1=Sik+1Si1
    联立得:
    T 1 ∼ T k − 1 = T j − k + 1 ∼ T j − 1 T _{1} \sim T _{ k -1}= T _{ j - k +1} \sim T _{ j -1} T1Tk1=Tjk+1Tj1
    k的解集为 K = { k ∣ k = 2 , 3 , . . . , j − 1 且  T 1 ∼ T k − 1 = T j − k + 1 ∼ T j − 1 } K=\{ k \mid k=2,3,...,j-1 \text { 且 }T _{1} \sim T _{ k -1}= T _{ j - k +1} \sim T _{ j -1} \} K={kk=2,3,...,j1  T1Tk1=Tjk+1Tj1}
    k越大,模式串滑动的距离越小,为了避免遗漏选取k的最大值 k = max ⁡ K k=\max K k=maxK

模式中的前k-1个字符(最大真前缀)与模式中 T j T_j Tj字符前面的k-1个字符(最大真后缀)相等时,模式T就可以向右"滑动"至使 T k T_k Tk S i S_i Si对准,继续向右进行比较即可。

在这里插入图片描述

滑动位置k仅与模式串T有关。
因此可以预先为模式串设定一个next数组,若令next[j]=k,则next[j]表明当模式串中的第j个字符与主串中相应字符“失配”时,模式串中需要重新和主串中该字符进行比较的字符的位置。
n e x t [ j ] = { 0 , 当 j = 1 时 k = max ⁡ K , 当 j ≥ 2 , k ≠ ∅ 1 , 当 j ≥ 2 , k = ∅ next[j]=\left\{\begin{array}{ll}0, & 当 j=1 时 \\ k=\max K, & 当 j\geq2, k\neq \varnothing \\ 1, & 当 j\geq2, k= \varnothing \end{array}\right. next[j]=0,k=maxK,1,j=1j2,k=j2,k=
其中 K = { k ∣ k = 2 , 3 , . . . , j − 1 且  T 1 ∼ T k − 1 = T j − k + 1 ∼ T j − 1 } K=\{ k \mid k=2,3,...,j-1 \text { 且 }T _{1} \sim T _{ k -1}= T _{ j - k +1} \sim T _{ j -1} \} K={kk=2,3,...,j1  T1Tk1=Tjk+1Tj1}
next[1]=0 表示T[1]与S[i]失配,下一步进行T[1]与S[i+1]的比较。
next[j]=1 表示 S i − k + 1 ∼ S i − 1 S _{ i - k +1} \sim S _{ i -1} Sik+1Si1的后缀都不为T的前缀:下一步进行T[1]与S[i]的比较。

KMP算法实现

int Index_KMP(SString S, SString T, int pos, int next[])
{// 利用模式串T的next函数求T在主串S中第pos个字符之后的位置的KMP算法
// 其中,T非空,1≤pos≤StrLength(S)。int i = pos, j = 1;while ( i<=S[0] && j<=T[0] ){  if ( j==0 || S[i]==T[j] ){++i; ++j; } // 继续比较后继字符else j=next[j]; // 模式串向右移动}if(j > T[0]) return i-T[0]; // 匹配成功,返回匹配模式串的首字符下标else return 0; //匹配失败
} // Index_KMP

求模式串T的next值算法

  1. 显然,next[1]=0, next[2]=1
  2. 如果next[j] =k,表明有 T 1 ∼ T k − 1 = T j − k + 1 ∼ T j − 1 T _{1} \sim T _{ k -1}= T _{ j - k +1} \sim T _{ j -1} T1Tk1=Tjk+1Tj1,此时next[j+1]可能有两种情况:
    (1). T k = T j T_k=T_j Tk=Tj,则 T 1 ∼ T k = T j − k + 1 ∼ T j T _{1} \sim T _{ k }= T _{ j - k +1} \sim T _{ j } T1Tk=Tjk+1Tj,因此next[j+1]=next[j]+1=k+1;
    (2). T k ≠ T j T_k\neq T_j Tk=Tj,则相当于在模式串 T 1 ∼ T k T _{1} \sim T _{ k } T1Tk与主串 T T T的模式匹配问题中k于j失配,设 k ′ = n e x t [ k ] k'=next[k] k=next[k]
    k ′ ≥ 2 k'\geq 2 k2,则有 T 1 ∼ T k ′ − 1 = T j − k ′ + 1 ∼ T j − 1 T _{1} \sim T _{ k' -1}= T _{ j - k' +1} \sim T _{ j -1} T1Tk1=Tjk+1Tj1,将k’赋给k并返回步骤2. ;
    否则说明 T j − k + 1 ∼ T j − 1 T _{ j - k +1} \sim T _{ j -1} Tjk+1Tj1的真后缀中没有 T 1 ∼ T k T _{1} \sim T _{ k } T1Tk的前缀,则next[j+1]=1
void get_next( SString T, int &next[] )
{// 求模式串T的next函数值并存入数组nextint i = 1, k = 0; next[1] = 0; while ( i < T[0] ) {	if ( k == 0 || T[i] == T[k] )    {  ++i;++k;     next[i] = k;     }else k = next[k];}
} // get_next 

时间复杂度分析

BF算法分析

最好情况下的时间复杂度为 O ( m ) O(m) O(m)
算法在字符比较不相等,需要回溯(即i=i-j+1):即退到s中的下一个字符开始进行继续匹配。
最坏情况下每趟比较m次,进行n-m+1趟匹配,总比较次数为m(n-m+1),时间复杂度为 O ( n × m ) O(n×m) O(n×m)。 平均时间复杂度 O ( n × m ) O(n×m) O(n×m)

KMP算法分析

设串S的长度为n, 串T长度为m,求next数组的时间复杂度为 O ( m ) O(m) O(m),在后面的匹配中因主串S的下标不减即不回溯,比较次数可记为n,所以KMP算法平均时间复杂度为 O ( n + m ) O(n+m) O(n+m)

KMP算法与BF算法比较

  1. 虽然朴素算法最坏时间复杂度为 O ( n × m ) O(n\times m) O(n×m),但一般情况下,其实际执行时间近似于 O ( n + m ) O(n+m) O(n+m),故仍被采用。
  2. KMP算法仅当模式与主串存在许多“部分匹配”时才比朴素算法快得多。
  3. KMP算法的最大特点是指示主串的指针i不需要回溯,这对处理从外设输入的庞大文件很有效,可以边读入边匹配而无需回头重读。

http://chatgpt.dhexx.cn/article/dilwa4iu.shtml

相关文章

英文打字速度180kpm

刚测了一下打字&#xff1b; 基本在180kpm左右&#xff1b; 作为程序员应该足够用了&#xff1b; 我看了一下kpm的意思&#xff1b; 请问英文的打字速度的KPM要多少才是一般录入员的速度呢?我177KPM是高还是低呀??? - ...... 一分钟要打60个字以上,大约要打420kpm,你的17…

4. 串的【朴素模式匹配算法】、【KPM算法:求next数组、nextval数组】

串的模式匹配:在主串中,找到与模式串相同的子串,并返回其所在位置。 其实就是给出一个串abc,找到abc在主串的位置【abc都要匹配】 模式串:给出一个串abc 子串:主串中的abc【可能没有】 文章目录 1. 串的朴素模式匹配算法1.1 方法一:用k记录位置1.2 方法二:不用k2. KPM算…

KPM算法详解(Next数组)

由LeetCode_28引发的思考 最开始用了剪枝思想的朴素解法虽然做出来了&#xff0c;在看答案的时候发现了有一种叫KMP的算法专门就是解决快速查找匹配串问题的&#xff0c;进行了深入的学习。 下面的图源自宫水三叶的解答 我们有两个字符串&#xff0c;在原串中寻找是否有匹配串。…

【C#】KPM算法解决字符串匹配问题

KPM算法解决字符串匹配问题 什么是KPM算法步骤Ⅰ根据《最大长度表》部分匹配表&#xff08;next&#xff09;寻找最长前缀后缀 Ⅱ 根据 部分匹配表 进行匹配 代码实现 什么是KPM算法 Knuth-Morris-Pratt 字符串查找算法&#xff0c;简称为 “KMP算法”&#xff0c;常用于在一个…

串的匹配 (KPM算法由来引导)

前言: 引入 KPM 算法的前提是 , B-F算法中,匹配失败后不必完全从头再来 , 找到可以利用的信息 , 可以进行跳跃性匹配 下面 , 我们对字符串匹配的一些思路进行剖析: 开始匹配的操作 ,我们会让 目标串 s , 和 模式串进行对齐,就像如图所示: 我们当然是从串 t 的头结点开始对比 对…

KPM匹配算法

KPM匹配算法 不管在工作中还是学习中经常会遇到串的模式匹配&#xff0c;一般来说&#xff0c;串的模式匹配算法主要是两种【朴素模式匹配和KPM模式匹配】一种改良【KPM的改良】&#xff0c;下面先看一下朴素模式匹配&#xff1a;朴素模式匹配主要思想比较简单也比较粗暴&…

KPM算法

算法 字符串匹配之朴素算法和KMP算法及JAVA代码实现 2017年06月02日 10:31:12 阅读数&#xff1a;941 暴力匹配算法 假设现在我们面临这样一个问题&#xff1a;有一个文本串S&#xff0c;和一个模式串P&#xff0c;现在要查找P在S中的位置&#xff0c;怎么查找呢&#xff1…

Python KPM算法

知识点说明&#xff1a; 先说前缀&#xff0c;和后缀吧 比如有一个串&#xff1a;abab 则在下标为3处的&#xff08;前缀和后缀都要比下标出的长度小1&#xff0c;此处下标为3出的长度是4&#xff09; 前缀为&#xff1a;a,ab,aba 后缀为&#xff1a;b,ba,bab 一、要获取…

KPM算法——数据结构|复习局|串|复杂模式匹配算法|二维数组解决KPM

数据结构复习局——KPM算法 何为KPM&#xff1f;事先规则状态匹配dp——状态转移图状态X获得dp数组值看看图再理解下 写在前面&#xff1a; 本文仅为作者个人学习记录&#xff0c;详细具体内容参考自知乎大佬labuladong &#x1f448;点击与大佬&#x1f93a;击剑&#x1f93a;…

KMP算法原理及实例

KMP是为了解决串的匹配问题。 一、基础知识 1、概念 1.串的匹配问题&#xff1a;就是在一个长的主串s中找到另一个的短的子串t。 2.目标串&#xff08;主串\文本串&#xff09;&#xff1a;被匹配的母串&#xff0c;串s被称为目标串。 3.模式串(子串&#xff09;&#xff1a…

KPM算法思想及实现

一、比较暴力算法和KMP算法 1、暴力算法&#xff1a; 双指针遍历主串和子串&#xff0c;当发现主串和子串不匹配时&#xff0c;双指针同时回溯 2、KMP算法&#xff1a; 双指针遍历主串和子串&#xff0c;当发现主串和子串不匹配时&#xff0c;通过比较子串的next数组只回溯…

正定矩阵的性质

正定矩阵的定义&#xff1a;若矩阵A是n阶方阵&#xff0c;并且它的二次型大于0&#xff0c;即&#xff0c;则矩阵A是正定矩阵。 正定矩阵的性质&#xff1a; 正定矩阵的所有特征值都为正数。正定矩阵行列式为正数两个正定矩阵的和为正定矩阵&#xff08;两个正定矩阵的乘积不…

顺序主子式

设A为 阶矩阵&#xff0c;子式 称为A的i阶顺序主子式。 [1] 对于 阶的矩阵A&#xff0c;其共有n阶顺序主子式&#xff0c;即矩阵A的顺序主子式由 共n个行列式按顺序排列而成。

【矩阵论笔记】平方根分解

定义 例子 首先判断顺序主子式。 R矩阵每一行除以对角元 平方根分解推导 因为A的分解是唯一的&#xff0c;可以得到 L R T LR^{T} LRT 例题 待定系数法

MATLAB_数值计算_用平方根法解线性方程组—(Cholesky分解)

利用对称正定矩阵的乔累斯基分解求解对称正定方程组的方法称为平方根法对称正定矩阵A的对角元为正实对称矩阵A正定的充要条件是A的所有特征值为正对称正定矩阵非奇异&#xff0c;其逆亦为对称正定矩阵实对称矩阵A正定的充要条件是A的所有顺序主子式为正正定矩阵的顺序主子阵是正…

正定与半正定矩阵,判别的方法不能混用,否则出错

坑&#xff1a;特征值有0可以知道该矩阵不可能正定&#xff0c;那可以用顺序主子式判断是否为半正定吗&#xff1f; 答案&#xff1a;不能 例子&#xff1a;A,特征值为一个0&#xff0c;两个正数>那就是半正定啦呀 但是&#xff0c;主子式二阶小于0那就不正定了呀两种方法…

线性代数【11】二次型

前言&#xff1a; 矩阵表达的是线性关系的方程组&#xff0c;但是&#xff0c;客官世界&#xff0c;是包括稍微复杂的&#xff0c;二次方程。 尤其几何中&#xff0c;有二次曲线&#xff0c;二次型&#xff0c;就是一个多元函数&#xff0c;是多个变量的二次&#xff0c;齐次…

Lyapunov稳定性分析1(正定函数、二次型正定判定)

一、 正定函数 1.1 定义&#xff1a; 令V(x)是向量x的标量函数&#xff0c;S是x空间包含原点的封闭有限区域。如果对于S中的所有x&#xff0c;都有&#xff1a; 则V(x)是正定的&#xff08;半正定&#xff09;。正定函数更直观的描述如下图所示&#xff1a; 如果条件(3&a…

牛顿法为什么要保证Hessian矩阵正定

牛顿法为什么要保证Hessian矩阵正定&#xff1a; 实际中&#xff0c;需要求出Hessian的逆矩阵&#xff0c;只有正定矩阵&#xff0c;才可以求出逆矩阵。 正定矩阵&#xff1a;特征值全大于0&#xff1b;各阶主子式全大于0.

正定矩阵的相关知识

一、正定矩阵的定义&#xff1a;若矩阵A是n阶方阵&#xff0c;并且它的二次型大于0&#xff0c;即 则矩阵A是正定矩阵。 二、正定矩阵的性质&#xff1a; 1.正定矩阵的所有特征值都为正数。 2.正定矩阵行列式为正数 3.两个正定矩阵的和为正定矩阵&#xff08;两个正定矩阵的乘…