写在最前:
KMP算法的核心思路:
问题由模式串决定,而不是由目标串决定!
此文前提是已掌握next数组算法!
问题引入:
假设模式串及其next数组:
模式串开始与目标串匹配:
模式串位置4与目标串位置4不匹配,所以下一步模式串指针应该按照next[4]移动:
但我们发现,这没有丝毫意义,刚刚就是因为模式串‘B’与目标串‘C’不匹配导致我们按照数组next调整指针位置,但调整后的指针所在位置模式串元素仍为’B’,自然仍不匹配,还要继续按照数组next回溯指针。
但是我们可不可以改进next数组来避免做类似无用功呢?
分析与解决:
于是诞生了nextval数组,同样的,我们假设模式串为:
**与next数组一致,如果位置1元素即不匹配,该位置nextval数组值为0**
(不是本文重要因素,故原因略去)
紧接着,假如位置1元素已匹配,位置2元素不匹配,按照位置2所对应的next数组值我们应回溯至位置1,但是我们发现位置1与位置2元素一样,所以说回溯之后仍是相同元素(非同一个)在与目标串比较,显然仍不匹配,就会继续回溯到位置1所对应的next数组值即0。
按照这个原理,我们设模式串的指针为k:
情况一:
特别地,我们发现位置1与位置2元素相同,在位置2不匹配(此时k=2)时我们按照next[2]=1,k回溯至位置1,像上文位置1元素与位置2元素相同,用模式串的位置1元素继续比较无意义,继续按照next[1]=0回溯指针,指针已到头,回溯结束,因此,我们记位置2对应的nextval数组值为0,此时next[2]=1而nextval[2]=0,两数组值不同。(记住这种情况的回溯过程,后文会引用)
情况二:
继续向下移动指针k,假设k移动到位置3,k之前元素已匹配,而k此时所指元素不匹配,那我们就指针回溯,按照位置3所对应的next数组值我们应回溯至位置2,这时我们发现情况和上述有所区别,位置2与位置3元素不相同,所以回溯后可能匹配成功(此时k=2),所以我们记位置3对应的nextval数组值为2,即nextval[3]=2=next[3],发现此时该位置next数组值与nextval数组值相同。
那何时next数组值与nextval数组值相同?又何时不同呢?
我们发现:当在k位置不匹配时,如果k位置所对元素与next[k]位置所对元素相同时,它们两数组值不同;而如果k位置所对元素与next[k]位置所对元素不相同时,它们两数组值相同。
好,现在我们发现了这个规律,接着上面的情况,继续让k指针向下移动,k移动到位置4(之前元素已匹配),假设此时不匹配,指针回溯到next[4]=1,而位置1元素与位置4元素相同,继续回溯next[1]=0,所以位置4所对nextval数组值为0(nextval[4]=0),与next[4]=1不同,符合上述发现。
乘胜追击,继续让k指针向下移动,k移动到位置5,假设此时不匹配,指针回溯到next[5]=2,而位置2元素与位置5元素相同,继续回溯next[2]=1,位置1元素与位置5元素仍相同,继续回溯next[1]=0所以位置5所对nextval数组值为0(nextval[5]=0),与next[5]=2不同,符合上述发现。
但是,“乘胜追击”的情况指针k都不止回溯了一次,两次的回溯出现了两次的元素相同,在指针从2回溯到1回溯到最终的0的操作,实际上也就是当k=2(上文情况一)时指针最终回溯至nextval[2]=0,所以说我们可以直接利用之前(并不一定是上一次)操作得出的nextval数组值来简化重复的操作。也就是说,如果位置k的元素与next[k]元素相同时,nextval[k]=nextval[next[k]]。
现在,我们已经验证并完善了“发现”关于“如果k位置所对元素与next[k]位置所对元素相同时,它们两数组值不同”,接着我们验证另一部分“如果k位置所对元素与next[k]位置所对元素不相同时,它们两数组值相同。”
继续下移指针k至位置7,假设之前元素已匹配,位置7此时不匹配,指针回溯至next[7]=4,位置4元素与位置7元素不同,此时位置4元素可能与目标串匹配成功,故nextval[7]=4,即nextval[7]=next[7]=4,太棒啦,“发现”的另一部分也被验证正确了。
结论:
如果位置k的元素与next[k]元素相同时,nextval[k]=nextval[next[k]]
如果位置k的元素与next[k]元素不同时,nextval[k]= next[k]
代码实现:
/*CaptainUniverse_ 2022.4.14*/
void get_nextval(String T,int *nextval)//T为模式串
{
j=0;//前缀
i=1;//后缀
nextval[1]=0;
while(i<T[0])
{
if(j==0||S[i]==T[j])
{
i++;
j++;
if(T[i]!=T[j])
{
nextval[i]=j;
}
else
{
nextval[i]=nextval[j];
}
}
else
{
j=nextval[j];
}
}
}
/*CaptainUniverse_ 2022.4.14*/
可略过部分:
没错,文章的图全是用Excel做的。
你能体会那种感觉吗?就是你理解了某知识,
但是在脑中你却无法组织语言把它描述出来,
即使描述出来别人也很难理解你的语言。
我的辛酸,谁懂?