今天我在学习KMP算法的时候,看到了一份令我十分钟意的代码。(当然这份是我自己打的,但大体不变)
void get_next(string s)
{
next[0]=-1;
int j=0,k=-1;
while(j<s.size())
if(k==-1||s[k]==s[j]) next[++j]=++k;
else k=next[k];
}
这是份你在网络上可以搜到的大致的构造next数组的代码。
他在那一整份代码中实际上是没有问题的。
你会发现,作为模式串的最后一个字符,它会默认继承前一个字符的next,不论它是否满足要求。但在主串与模式串的匹配过程中,最后一个字符的next没有影响,因为不管是否匹配都要移动到前面的某一位,不行可以接着再移。
比如说模式串为abab,这样的next数组为{-1,0,1,2}。
但在构建next数组时,我们对最后一个字符并没有做处理。在if语句中,我们对某位的字符匹配,操作的却是后一位字符的next。很奇怪吧,但是没有问题,因为最后一位不重要。与主串不匹配,就不取它嘛,无论如何依然向前移动嘛。(可以自己去推推)
比如说主串与模式串匹配过程如下:
int work_KMP(string s,string p)
{//不要在意,这里的目的是统计匹配的子串数,不是返回位置。
int i,j,res;i=j=res=0;
while(i<s.size())
{
if(j==-1||s[i]==p[j]) ++i,++j; else j=next[j];
if(j==p.size()) ++res, j=next[j];
}
return res;
}
于是乎,当我们最后一位字符改变时,比如abad,它的next数组依旧为{-1,0,1,2}!!当我做另外一题时,便发现这个写法不仅不清楚,而且存在这个致命的错误。要改的话,不如打另一种版本的。
这与KMP算法的思想是不符的。
我开始也挺蒙圈的,搞懂(可能还没有完全搞懂,欢迎指正)了以后,我选择一本通高效进阶的这个代码:
inline void pre()
{
p[1]=0;
for(int i=1,j=0;i<m;++i)
{
while(j>0&&b[j+1]!=b[i+1])
j=p[j];
if(b[j+1]==b[i+1])
++j;
p[i+1]=j;
}
}
或者这个大佬的代码:KMP算法 详解+模板 - cervusky
void getnx()
{
nx[1]=0;
for(int i=2,j=1;i<=n;)
{
nx[i]=j;
while(j&&s1[j]!=s1[i])j=nx[j];
j++,i++;
}
}
这就十分的清晰明了,且十分符合KMP算法的思想。(当然了,我觉得高效进阶里的代码都着实不太行,但这里姑且还清楚)