个人柑橘KMP算法并不难,但是理解起来确实麻烦。稍微记一下,免得日后遗忘。
KMP算法优于BF算法就在于引入了next数组从而使子串与目标串的同步速度加快从而
优化了时间。
next数组是个人感觉最难理解的地方,也是用了一段时间才搞懂,现记录一下。
next数组何用:
next数组用来在子串与目标串的匹配中使子串更快的移动。其中next[j](下标从0
开始)表示前j个元素中(即下标到j-1)**包含第j个元素(下标j-1)的后串和带第一
个元素的前串相等的最长的子串的长度**(定语太多不好理解,举个例子就懂了,
其中next[0]默认等于-1,next[1]默认
等于0(以后解释):
s[8]=a b c a b c a b
next[]=-1 0 0 0 1 2 3 4 0
next[3]即前三个a b c中包含c的最长的前后相等的子串长度,显然没有,为0.
next[4]即前四个a b c a中包含a的最长的前后相等的子串长度,即第一个
元素a(下标0)与第四个元素a(下标3)为最长的相等的子串,长度为1;
next[6]即前6个a b c a b c中包含c的最长的前后相等的子串的长度,即前三个a b c
和后三个a b c。
next[7]同理,是前四个a b c a和后四个a b c a。
next数组如何求,先放上代码
int * Get_Next ( Str & b)
{
int * next= ( int * ) malloc ( sizeof ( int ) * ( b. length+ 1 ) ) ;
next[ 0 ] = - 1 ;
int k= - 1 ;
int j= 0 ;
while ( j<= b. length)
{
if ( k== - 1 || b. str[ k] == b. str[ j] )
next[ ++ j] = ++ k;
else
k= next[ k] ;
}
return next;
}
看一下,感觉应该不难理解,个人建议先按照上边的例子🌰,自己先跑一下整个next数组的过
程,再看下面的解释。
我想应该不难,除了最后的`else k=next[k]`这确实是个老大难的问题。
放个图理解一下。
现在假如下标为0 1 2的串已与下标为j-3 j-2 j-1的串相匹配,即next[j]=3,则下一步应该比
较下标为3的元素与下标为j的元素,显而易见,若str[3]==str[j],则str[++j]应当赋值为++k。
(如果你看了前面的代码并自己摸你了一遍,现在应该知道k是什么,k即为前串最长的串的
下标),倘若str[3]!=str[j],这是k=next[k]又是为何呢!现在想,倘若要在前j+1个元素中
(即下标为j+1前)寻找包含第j+1个元素(下标为j)的子串,next[j+1]结果无非是0或者非0,
为0时即str[0]与str[j]不想等,否则子串中一定包含元素str[j],可是元素str[3]与str[j]不等,
接下来该如何找呢!想象一下极端情况,倘若next[j+1]==1;则子串一定是str[j]与str[0]。倘
若有第二个元素令next[j+1]==2,则一定是str[j-1]与str[0]相等,srt[j]与str[1]相等,依次类
推。
可是在寻找next[j+1]之前,str[j-1]是否已经与前面的某个元素相匹配了呢?是的,那个元
素的下标就是k-1,可是又不能用k-1(因为str[k]与str[j]不想等),之前还有没有一个与
str[j]相等的元素呢?答案肯定是有的。str[j-1]==str[k-1]==str[next[k]-1](想一想为什
么)。 str[j-1]==str[next[k]-1],同理,下标为j-2的元素也已经在之前匹配过了,依次类
推。 我们接下来只需要比较str[j]和和str[next[k]]是否相等即可,倘如不想等,继续令
k=next[k],直到找到相等的串,若找不到,则为-1。
这一段比较晦涩难懂,建议静下心来慢慢读一读想一想,多去找找资料。倘若next数组解
决了,其余的自然不成问题。
总代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef struct list
{
char str[ 101 ] ;
int length;
} Str;
int * Get_Next ( Str & b)
{
int * next= ( int * ) malloc ( sizeof ( int ) * ( b. length+ 1 ) ) ;
next[ 0 ] = - 1 ;
int k= - 1 ;
int j= 0 ;
while ( j<= b. length)
{
if ( k== - 1 || b. str[ k] == b. str[ j] )
next[ ++ j] = ++ k;
else
k= next[ k] ;
}
return next;
}
void Find_Location ( Str & a, Str & b, int * next)
{
int i= 0 , j= 0 ;
while ( i< a. length&& j< b. length)
{
if ( j== - 1 || a. str[ i] == b. str[ j] )
{
i++ ;
j++ ;
}
else
j= next[ j] ;
}
if ( j== b. length)
cout<< "Find is:" << i- j+ 1 << endl;
else
cout<< "Not Find" ;
return ;
}
int main ( )
{
Str a, b;
cout<< "in str1:" ;
cin>> a. str;
a. length= strlen ( a. str) ;
cout<< "in str2:" ;
cin>> b. str;
b. length= strlen ( b. str) ;
int * next= Get_Next ( b) ;
Find_Location ( a, b, next) ;
return 0 ;
}