KMP 算法简单解释
讲KMP算法,离不开BF,实际上,KMP就是BF升级版,主要流程和BF一样,就是在削除回溯上花了点功夫,利用Next数组来削除
<( ̄︶ ̄)↗[GO!]
1. 先看看BF算法(暴力破解)
int Brute_force_1(const char *S, const char *T)
{
if (!S || !T)
return -1;
int n = strlen(S);
int m = strlen(T);
int i = 0; //主串下标索引
int j = 0; //子串下标索引
while(i < n && j < m)
{
if (S[i] == T[j]) //如果相等一直继续往下匹配
++i,++j;
else //不相等i和j开始回溯
{
i = i-j+1;
j = 0;
}
}
if (j == m)
return i - j;
return -1;
}
BF算法有几种不同实现,但最终思想都是一样的,以下就是另一个BF实现
int Brute_force_2(const char *S, const char *T)
{
if (!S || !T)
return -1;
int n = strlen(S);
int m = strlen(T);
for (int i = 0; i <= n - m; ++i)
{
int k = i, j = 0;
while (k < n && j < m && S[k] == T[j])
{
++j;
++k;
}
if (j == m)
return i; //说明匹配到了
}
return -1;
}
你完全可以根据自己的理解写出BF算法,但在这里,为了BF和KMP统一,我们还是采用第一种实现,即容易看出回溯操作的实现
2. Next[]数组
事实上,书上的next数组生成算法是经过优化后的算法,比较难懂,但你完全可以按照自己的理解做一个
注意:Next[]数组只是在KMP中字符串匹配失败时使用的
void GetNext(int Next[], char *str)
{
assert(str!=NULL);
int len = strlen(str);
if(len>1)Next[0]=-1;
//只有Next[0]为-1,当第一个不匹配时则在KMP中讲主串索引i向后挪一位(++i);
//此时子串索引下标j=0,(j也为Next[]数组下标索引)
if(len>2)Next[1]=0;
//Next[]等于0时说明需要讲i回溯到子串头的下一个位置(i=i-j+1);
//此时j也回到子串头位置(j=0)
for(int i=2;i<len;++i)
{
for(int j=i-1;j>0;--j)
{
if(!strncmp(&str[0],&str[i-j],j))
{
Next[i]=j;break; //找到最大重复子子串(子串中的子串)
//Next[]为其他值则i不变,讲j回溯到Next[j]的位置(j=Next[j])
}
else Next[i]=0;
}
}
}
这个时间复杂度要比书上的方法高很多,但好理解,真实的反映了Next数组的本质。
3.KMP
int KMP(const char *S, const char *T, const int *Next)
{
if (!S || !T||!Next)
return -1;
int n = strlen(S);
int m = strlen(T);
int i = 0; //主串下标索引
int j = 0; //子串下标索引
while(i < n && j < m)
{
if (S[i] == T[j]) ++i,++j; //若相等则继续匹配下一个字符
else //不相等则回溯
{
//(当Next[j]的值为-1和0时事实上与BF算法相同)
if (Next[j] == -1||Next[j]==0)
{
i = i-j+1;
j = 0;
}
else j = Next[j];
}
}
if (j == m)
return i - j;
return -1;
}
这个回溯时的操作实际上是把两种情况合成一种,拆开后就是下面的,就是生成next数组那块三种情况
while (i < n && j < m)
{
if (S[i] == T[j]) ++i,++j;
else
{
if (Next[j] == -1)
{
++i;
}
else if (Next[j] == 0)
{
i =i-j+1;
j = 0;
}
else
{
j = Next[j];
}
}
}
总结
其实核心就在与本文第一句话的理解。书上的next数组的生成很难懂,加油理解中。。。(ง •_•)ง