KMP模式匹配算法之next数组解析

版权声明:欢迎转载,转载时请注明出处~ https://blog.csdn.net/Yoosona/article/details/77170768

一,什么是kmp模式匹配算法?

百度百科: KMP算法是一种改进的字符串匹配算法,由D.E.Knuth与V.R.Pratt和J.H.Morris同时发现,因此人们称它为克努特–莫里斯–普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。也叫看毛片算法,总之贼厉害。

二,自己理解之后的next数组

近两天在学习这个算法时,开始觉着还好,到了这个next数组时,书上虽然分析了一波next数组的由来,然后直接上代码,但是第一次看属实没看懂,智商是硬伤,然后查阅了相关资料,算是明白了过程。

前面的算法原理以及它相对朴素算法简略的步骤在这里就不说了,比较容易理解,大概意思就是对于子串中首字符与后面的字符均不相等时,或者字串中有与首字符相等的字符就可以省略一部分的判断步骤。

在之前的朴素的模式匹配算法中,主串的i值是不断回溯来完成的,而 KMP算法就是为了让这没必要的回溯不发生,所以要考虑的就是子串中的j值如何变了(j值与主串是没有多大关系的,这点要明白),而j值的变化就定义为一个数组next,其长度就是T串的长度。

三,此处才是重点!

我们知道如果主串与子串如果在某个字符不匹配,下一个与主串匹配的字符是谁(即j的值)取决于当前子串不匹配字符的前面字符前后缀的相似度。

T 9 a b a b a a a b a
下标j 0 1 2 3 4 5 6 7 8 9
next

比如这里有个模式串T,此处串T当成以数组的形式存储,所以下标j==0时一般储存的是数组长度9。

T 9 a b a b a a a b a
下标j 0 1 2 3 4 5 6 7 8 9
next 0 1 1 2 3 4 2 2 3

下面开始分析next数组的值如何得来:

首先定义T【i】为模式串中不匹配字符之前字符的后缀的单个字符,T【j】为前缀。
当T串的第一个字符就与S串的第一个字符不同时,此时T串第一个字符就与S串的第二个个字符对应,也就相当于T串整体往后移一位,而此时对应S串第一个字符正好可以看成是下标j==0时的T值9,所以有next[1] = 0.与表格结合起来意思就是第一个字符a与S串不匹配,下一步就是让代表T中第0个位置(也就是9)的代表a与S串的字符匹配,a与S串的下一个匹配。

当i=1,j=0时,存储的是字符串T中的数组长度为9,没多大意义,执行 ‘++i;++j’ ,然后

if(j==0 || T[i]==T[j]) //T[i]表示后缀的单个字符,T[j]表示前缀的单个字符
{
  ++i;
  ++j;
  next[i] = next[j];
}
else
  j = next[j];  //若字符不相同,j值回溯

按照上面的代码当i=2,j=1时,根据表格T[2]=b≠T[1]=a,所以执行代码中j = next[j] =next[1]=0 (这样写只是为了方便理解),然后回到循环执行++i;++j;后此时i=3,j=1,然后执行next[3]=j=1,继续T[3]=a=T[1],然后++i;++j; 此时i=4,j=2,next[4]=j=2,这里的2的意思是如果在T串中的第i=4个字符b与主串S不匹配时,下一步就是用T串中的第next[4]=2个字符来代替b与S串匹配,然后再继续就可得出表格中的值,此处不一一写出。

此处要注意的是当下标为4时,此时字符b前面的字符为aba,前缀为a,后缀也为a,而next[4]=2; 当下标为5时,此时字符a前面的字符为abab,前缀为ab,后缀也为ab,而next[5]=3; 当下标为6时,此时字符a前面的字符为ababa,前缀为aba,后缀也为aba,而next[6]=4,不难发现next数组的值比前后缀相同个数大一。当T[6] ≠ T[4]时,其我们知道执行的是j值回溯,其实这个回溯的原理和T串匹配S串的原理是差不多的,它相当于T串与自身的匹配,当两者不等时,其回溯的目的就是为了找出字符串前后缀相等的个数,从而得出下一步该用T串哪一个字符(即下标对应next的值)与S中字符去匹配。可以根据表格自行设置S串的字符去验证加深理解。

以下附上完整的代码:

void get_next(String T, int *next)
{
    int i, j;
    i = 1;
    j = 0;
    next[1] = 0;
    while ( i < T[0] ) //T[0]代表串T的长度
   {
    if(j==0 || T[i]==T[j]) 
     {
       ++i;
       ++j;
       next[i] = next[j];
     }
else
  j = next[j];  //若字符不相同,j值回溯
  }
}

没有更多推荐了,返回首页