KMP算法
我们知道,KMP算法是用来匹配字符串的:有一个模式串(patter)ss(长的那个串),然后有一个待匹配串s, 然后我们要判断s 是否在ss中,KMP算法就高效解决这个问题。其中,最关键的就是求next[]数组了。
我们实质上是在找一个模式串(pattern)str0中的每个位置i 的nexti,前i子串内的最长的前缀与后缀,next[i]是这么定义的:【0,next[i]-1】的子串与【i-next[i],i-1】的子串相同,且使得next[i]最大;换言之,就是【0,i-1】子串的前缀与后缀相同的最大长度,即next[i]。当然,最符合next[]数组的原本解释,应该是这样的: 如果在i位置不匹配了,则在保证原串(长的那个)的 j位置不回溯的前提下,得到待匹配串的匹配起始位置next[i]。
void get_next(const string& s,int next[])
{
int i=0; //扫描s时的位置
int j=-1; //前缀位置
next[0] = -1;//开始时,没有前缀,没有后缀
while(i<s.length()){
if(j == -1||s[i] == s[j]){
//如果匹配成功,则继续向右移动,找下一个位置的最大的前缀与后缀相等
j++; //右移
i++; //右移
next[i] = j; //记录该位置的长度
}
//如果不匹配了,则缩短长度:在不移动i的前提下,移动j来进行新一轮匹配
else j = next[j];
}
}
改进版
- 上面的算法是有缺陷的,主要体现在下面例子中:
-
- 会发现,这样移动是没有意义的,因为 C != B;应该直接移动到 -1位置,改进版算法其实很简单:
void get_nextval(const string& s,int next_val[])
{
int i=0;
int j=-1;
next_val[0] = -1;
while(i<s.length()){
if(j == -1||s[i] == s[j]){
j++;
i++;
if(s[i] == s[j]){
next_val[i] = next_val[j];
}
else next_val[i] = j;
}
else j = next_val[j];
}
}
相等的时候,就跳过,直接把next_val[i]回到前面去,这是因为,我们要回到next[i]最初定义中去,是说:不匹配时,移动回去,即当ss[k](这是匹配时,较长的那个串) != s[i]时,既然ss[k] != s[i],自然也不会等于s[j] ,所以往前移到next_val[i] = next_val[j] 。
发现KMP算法真是好难理解啊,真的想了很久,才搞懂,发现它的代码实现真的很神奇!神奇的KMP!