再次学习kmp
大学学数据结构的时候学的KMP,看了两个星期代码,就是有空就看看代码,琢磨琢磨明白了。没有看教材上的解析是因为那个解析太生涩了!
KMP的难点在于next数组的求解过程
next[i]存储的是什么,存储的next[0 ~ i-1]这个字符串后缀与前缀最大的相同长度(设为value_l)
这样当在 b[i]匹配失败的时候,转到下标value_l处继续匹配 //b[value_l] 和 b[i]不相同,但是它们前面长度value_l的字符串却相同。
如何快速求出next数组呢?
先初始化一下
next[0] = -1; //b[0]匹配失败的时候直接后移,重新开始。不为-1就是移动b,而不是调整b的位置
int i = 1; //要求解的next[i] 的 i
int j = 0; //这是一个游标,表示next[0,i-1]前后缀最大的公共长度
如果b[i] == b[j]
那么已经知道b[i]如果匹配失败转移到哪里去了,转移到next[j];因为最后一个字符相同导致现在是j+1个字符相同,转到j同样也会失败.
//所以说这句代码可以这么些 next[i] = next[j];
i和j 都皆大欢喜,携手往前一步 i++, j++; //公共长度+1 再接再厉求下一个结果
如果不相同呢
那么已经知道b[i]如果匹配失败转移到哪里去了,转移到下标j
//下次要计算next[i+1]的,它的最大前后缀公共长度j肯定需要调整的,我们就在这里收拾这个烂摊子
j, next[j], next[next[j]], next[next[next[j]]], …, 0 j这样依次往前跳,这些点的共同特征是什么
所有 有可能 和next[0~i]的后缀相同的前缀都在这里了!!! 说的是b[0, j]们
其实也不是都在这里,但是所有合适的前缀的都在这个列表里
即使有漏网之鱼,i和j协同前进的步伐肯定碾压过它们,但是它们为什么被忽略了呢?//说的就是 next[i] = next[j] 里面的 j 不会出现在这个枚举列表里
关键点就在于它们的最后一个字符必须要和b[i]对上,但是和它最后相同的但是更长的都在枚举列表里面,如果那个都没被选上,那么 这个j直接被忽略就太合理了
所以调整j的位置就是枚举呀
然后j都为求next[i+1]调整好了,i也就可以往前走一步了
void cal_next(char* b, int* next, int length)
{
int i = 1;
int j = 0;
next[0] = -1;
while(i<length)
{
if(b[i] == b[j]){
next[i] = next[j];
i++, j++;
}else{
next[i] = j;
//找到相同的公共部分为了next[i+1]的计算
while(j != -1 && b[j] != b[i])
j = next[j];
if( b[i] == b[j] )
i++, j++;
else
i++;
}
}
}
记得在数据结构里面有代码形式更简洁妖艳的写法,我个人对那段代码是极度拜服。。 但是那段形式上最简化的代码(所有while跑的次数没有优化),并且却不便与这样理解。 贴网上的不保证正确
void get_next(string b, int *next)
{
int i=0;
int j=-1;
next[i]=-1;
int len_b=b.size();
while(i<len_b-1)
{
if(j==-1||b[i]==b[j])
{
++i;
++j;
if(b[i]!=b[j])
next[i]=j;
else
next[i]=next[j];
}
else
{
j = next[j];
}
}
}