KMP算法详解

KMP算法

此篇主要讲原理,抛开代码,从原理的层面上来理解整个算法。

首先概括一下KMP算法的主要内容:
KMP算法是一种字符串匹配算法,KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next数组,数组的意思是指向模式串的下一个开始位置。时间复杂度O(m+n)。

KMP算法的主要计算过程,代码如下:

int KMP(char S[], char T[], int next[], int pos){
  //利用模式串T的next函数求T在主串S中第pos个字符之后的位置的KMP算法。
     int i = pos;
     int j = 1;
     while(i<=S[0] && j<=T[j]){
       if(j==0 || S[i]==T[j]){
           ++i;
           ++j;
         }//if
       else
       j = next[j];
     }//while 
     if(j > T[0])
       return i- T[0];
     else
       return 0;
 }

上面的代码很好理解,如果S[i] == T[j],表示当前位置的主串和模式串中的字符相同,i j各自加一;如果不同,j = next[j],表示S[i]!=T[j]的时候j指针下一步移动的位置。这里j的不是从T数组的初位置开始,而是一个恰当的位置

KMP算法的难点在于next数组的建立,代码如下:

void get_next(char T[], int next[]){
    int i = 1;
    next[1]=0;
    int j = 0;
    while(I <= T[0]){         //这里的T[0]存储T的个数
     if(j==0 || T[i]==T[j]{
          ++i; ++j; next[i] = j; 
     }//if
     else
        j = next[j];
   }//while
}

整个KMP算法的核心就是这个next数组的建立,也就是确定当一个字符与主串不匹配的时候,我们要知道j这个指针要移动到哪里去

我们首先来发现一下规律。

当i j运行到如上位置的时候,S[i] != T[j],这时候我们会想到的解决办法就是

很明显,因为i的位置的前面有s,我们就把j的位置移动到2上。那么,规律来了:
当匹配失败的时候,j要移动的下一个位置k,k应该满足这样的条件:
k前面的k-1个所有字符要和j前面的k-1个字符完全一致。
数学公式如下:
T[1 … k-1] = T[j-k+1 … j-1]
例子中

我们后来选取的k的位置的前面所有的字符和j前面k-1个完全一样(这里k=2),都是s。

我们来推导一下数学公式,来证明一下为什么直接将j移动到k的位置,而不是第一个位置。
当S[i] != T[j]时
S[i-j+1 … i-1] = T[1 … j-1]
T[1 … k-1] = T[j-k+1 … j-1]
=> S[i-j+1 … i-1] = T[1 … k-1]
所以直接保证了k的前k-1个字符串和S中i之前的k-1个字符完全一样。

所以我们的的目的就是找到匹配不成功的时候最合适的k的位置,这个k的位置要保证前k-1个字符和主串中i之前的k-1个字符相同且尽可能最长
这里写图片描述
当出现这种情况的时候,我们的k应该是1还是4,很明显是4,我们希望的是这样:
这里写图片描述
而不是这样:
这里写图片描述
因为我们就是为了让k尽量的离初始位置远一些,也就是前k-1个位置的所有的字符串和主串中i之前的k-1个字符串的完全一致。

我们接着来归纳:

首先来看第一个,当j在第一个位置就和主串不匹配的时候:
这里写图片描述
S[i] != T[j],因为j = 1,j已经在最左边了,所以,j的下一个位置只能是0,即next[[1]] = 0
这里写图片描述
而当j = 2,主串和模式串不匹配时:
这里写图片描述
此时j也就只有一个位置可以移动,那就是第一位,即next[[2]] = 1;

说这么多,可能有些同学还是没有理解,所以我们来用一个例子来说明一下。

eg:求“ababaaababaa”的next数组(from 王道论坛-2017数据结构 P269 第六题)

这里写图片描述

首先根据之前对j = 1,j = 2位置的讨论,next[1] = 0, next[2] = 1;这里应该没有疑问,那么接下来我们讨论第三个。
第三个位置T[3] = a,这时候前三个字符串为aba,所以第三个匹配不成功的时候,主串中S[i-2] = a, S[i-1] = b,在模式串中找不到这样的k,使得前k-1个位置的字符分别是ab或者是b,所以j= 3的下一个位置k只能为1,即next[3] = 1,大家不要忘记了,next[j]的值就是下一个要匹配的k的位置,这一点很重要,做题过程中要始终记得

这里写图片描述

当j = 4的时候,这时候和主串中已经匹配了的字符串一定是aba,所以当第四个位置不匹配的时候,我们下一个要找的k的位置就一定满足,前k-1个字符串为aba或者ba或者a(我们要找能满足条件的最长的子串,即k值尽可能的大),我们观察T数组中的前三个,只有当k为2的时候,前k-1个字串为a,即为满足条件的最长,我们来结合图片看一下

这里的主串只是为了更容易理解,所以怎么简单怎么来,前后可能不一致

即k位置的前k-1个和i前k-1个的字符完全一样,next[4] = 2;

这里写图片描述

接下来第五个位置,当T[5] != S[i]的时候,说明主串和模式串中当前位置的前四个都是已经匹配了的,即为abab,所以我们接下来找的位置k要满足前k-1个为abab、bab、ab、b中的满足条件的最长的。所以我们发现k = 3的时候,前两个为ab即为满足条件的最长的
这里写图片描述

所以此时next[5] = 3,即j = 5不匹配的时候,j的下一个位置为3

这里写图片描述

同理,第6个位置不匹配的时候,说明前面已经匹配了5个了,即ababa,所以接下来找的k满足前k-1个为ababa、baba、aba、ba、a中最长的,观察得:k = 4的时候,前k-1个为aba为满足条件的最长的串

这里写图片描述

此时的next[6] = 4,第6个位置不成功时的下一个查找位置为4

这里写图片描述

j = 7时,前面已经匹配了6个,即ababaa,所以满足条件的有ababaa、babaa、abaa、baa、aa、a中最长的,观察,k = 2时,前k-1个为a为最长的

这里写图片描述

此时next[7] = 2,第7个位置不成功时的下一个开始位置为2

这里写图片描述

j = 8时,前面已经匹配了7个,即ababaaa,所以满足条件的有ababaaa、babaaa、abaaa、baaa、aaa、aa、a中最长的,观察,k = 2时,前k-1个为a为最长的

这里写图片描述

此时next[8] = 2,第8个位置不成功时的下一个开始位置为2

这里写图片描述

j = 9时,前面已经匹配了8个,即ababaaab,所以满足条件的有ababaaab、babaaab、abaaab、baaab、aaab、aab、ab、b中最长的,观察,k = 3时,前k-1个为ab为最长的

这里写图片描述

此时next[9] = 3,第9个位置不成功时的下一个开始位置为3

这里写图片描述

j = 10时,前面已经匹配了9个,即ababaaaba,所以满足条件的有ababaaaba、babaaaba、abaaaba、baaaba、aaaba、aaba、aba、ba、a中最长的,观察,k = 4时,前k-1个为aba为最长的

这里写图片描述

此时next[10] = 4,第10个位置不成功时的下一个开始位置为4

这里写图片描述

j = 11时,前面已经匹配了10个,即ababaaabab,所以满足条件的有ababaaabab、babaaabab、abaaabab、baaabab、aaabab、aabab、abab、bab、ab、b中最长的,观察,k = 5时,前k-1个为abab为最长的

这里写图片描述

此时next[11] = 5,第11个位置不成功时的下一个开始位置为5

这里写图片描述

j = 12时,前面已经匹配了11个,即ababaaababa,所以满足条件的有ababaaababa、babaaababa、abaaababa、baaababa、aaababa、aababa、ababa、baba、aba、ba、a中最长的,观察,k = 6时,前k-1个为ababa为最长的

这里写图片描述

此时next[12] = 6,第12个位置不成功时的下一个开始位置为6

这里写图片描述

至此,整个过程就结束了,next数组就这样求出来了。是不是很简单!!!我这篇博客写的挺多,后面将一整个例子完完全全的一步一步的推导出来,所以篇幅比较长,不过,只要你耐下心来看,应该可以看懂的。

这是我第一次写博客,所以经验不足,表达能力欠佳,我已经用我认为最简单的想法来解释算法了,但是如果有不足的地方,请各位看官指正,互相学习。

<think>好的,用户想要了解KMP算法的详细解释和使用方法,包括实现步骤和示例代码。我需要根据提供的引用资料来整理回答。首先,KMP算法的核心思想是通过预处理模式串生成next数组,避免回溯主串指针,提高匹配效率。接下来,我需要分步骤解释算法流程,比如如何构建next数组,如何进行匹配过程。然后,给出一个具体的示例代码,可能用Python来实现,因为用户之前的示例也是Python。还要注意用户提到的系统级指令,比如数学公式的格式,独立公式用$$...$$,行内用$...$。同时,回答中需要自然地添加引用标识,比如在提到优化效率时引用[^2]。最后,生成相关问题,比如next数组的构建方法、时间复杂度分析等。需要确保LaTeX语法正确,比如模式串的前缀函数可能用数学表达式表示。检查是否有遗漏的步骤,比如部分匹配表的生成过程,确保逻辑清晰,结构合理。</think>### KMP算法详解及实现 #### 一、核心思想 KMP算法通过**前缀函数**预处理模式串生成$next$数组,使得匹配失败时能跳过无效比较。其核心是保持主串指针$i$不回溯,仅调整模式串指针$j$,将时间复杂度从暴力算法的$O(n \times m)$优化到$O(n + m)$[^3]。 #### 二、实现步骤 1. **构建next数组** - 定义:$next[j]$表示模式串$P[0..j]$中最长相等前后缀长度 - 递推公式: $$ next[j] = \begin{cases} -1 & j=0 \\ max\{k \mid 0 \leq k<j \text{ 且 } P[0..k] = P[j-k..j]\} & \text{其他情况} \end{cases} $$ - 示例:模式串"ABABC"的$next$数组为$[-1,0,0,1,2]$ 2. **匹配过程** - 初始化$i=0$, $j=0$ - 当$i < \text{主串长度}$且$j < \text{模式串长度}$: - 若$j=-1$或主串$T[i] = P[j]$,则$i++, j++$ - 否则$j = next[j]$ - 当$j \geq \text{模式串长度}$时匹配成功 #### 三、Python实现示例 ```python def kmp(text, pattern): n, m = len(text), len(pattern) next_arr = get_next(pattern) i = j = 0 while i < n and j < m: if j == -1 or text[i] == pattern[j]: i += 1 j += 1 else: j = next_arr[j] return i - j if j == m else -1 def get_next(pattern): m = len(pattern) next_arr = [-1] * m k = -1 for j in range(1, m): while k >= 0 and pattern[j] != pattern[k+1]: k = next_arr[k] if pattern[j] == pattern[k+1]: k += 1 next_arr[j] = k return next_arr ``` #### 四、示例说明 主串$T= "ABABABABC"$,模式串$P= "ABABC"$时: 1. 生成$next=[-1,0,0,1,2]$ 2. 匹配失败时通过$next$数组跳过冗余比较 3. 最终在第4次调整后匹配成功[^1]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值