KMP算法详解

来源:互联网 发布:js注释规范 编辑:程序博客网 时间:2024/06/06 13:02

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

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

0 0