KMP算法很早就接触了,记得自己看着代码也能理解实现,但是时间已久总是忘了如何编码,也许是自己对于next函数的理解一直停留在记忆层面上。
现在把KMP的next函数拿出来,看看能不能分析出一个门道,理解了它的逻辑,方式,才会记得比较熟悉吧。
KMP的思想这里不探讨了,直接上next函数的代码来分析。
void next(const char *T, int next[])
{
int j = 0, k = -1;//两个位置指针,j表示当前匹配指针。
//j,k都代表了当前重复子串后的位置。即图(1)的F和D位置
next[0] = -1;
//把第一个的位置置为-1,表示从头开始匹配
while ( T[j] != '\0' )//while循环
{
if (k == -1 || T[j] == T[k])
{
++j; ++k;
if (T[j]!=T[k])
next[j] = k;
else
next[j] = next[k];
}
else
k = next[k];
}
}
个人理解,这个next函数有两个目的,1.求一个最长重复序列;2.根据最长序列求回溯位置。
1.最长重复子序列:
比如ABCDABC,这个序列的最长重复字序列式ABC,当然,这里的最长重复要满足要求,前一个顶头,后一个顶着尾巴。如下图(1)所示。
CABCDABC,这个序列ABC子序列不满足前一个顶头的要求,所以这个序列的最长公共子序列是C。
2.回溯位置。如图一,F之前的最长子序列求出来以后,我们目的是计算F的回溯位置。按照理解,我们匹配失败后,可以让F回溯到D的位置,因为它们的前缀是相同的。
程序里还加了一个判断条件,就是继续还要判断F与D是否相等,如果相等,即都为F,那么F失败的时候回溯回来也是错误的,那么我们就然F指向D之前的位置。
图(1)
于是我们知道了程序的两个步骤,1,求最长子序列;2,求有效回溯位置(注意是有效);图(3)是程序的一个流程图:
图(2)
接下来,自己总结下next函数的思想。
next是一种DP的思想,即动态规划。next和k指针都记录了以前计算的结果,next为前面元素匹配位置,k记录当前最长字串,用于后面的计算。其实我们也可以写一个简单寻找最长重复序列的非DP算法,不过那会很费时。