-
相信已经提出这个问题的小伙伴已经对KMP算法有一定的了解。
-
当然不了解的话,建议大家可以先去掌握一下KMP算法。
-
因为这个题目我认为基于KMP算法是比较好做的。
-
下面是我个人想出的时间复杂度为O(n)的算法。
首先
KMP算法中 next 函数(即next[ j ]表示字符串S中位于S[ j ] 之前的最长相等前后缀的长度)
把next 函数代码放在这里了:
void BuildNext(string P, int next[], int m) {//m表示P的长度
int k = next[0] = -1;//设初值,k表示当前字符P[i]之前的字符串的最长相等前后缀的长度
for (int i = 0; i < m; i++) {
while (k >= 0 && P[i] != P[k])
k = next[k];
next[i + 1] = ++k;//如果k=-1跳出循环,则说明前面没有最长相等前后缀,next[i+1]=0
//如果P[i]=P[k]跳出循环,说明next[i+1]=next[i]+1;
//同时k自加,保持当前到P[i+1]为止的最长相等前后缀长度的最新的数值
}
}
其次
我们知道,
最短前后缀必然会出现在最长相等前后缀中(要么一致,要么更短)。
如果最长相等前后缀为0,那最短前后缀必然也为0。
我们就直接在 next 函数中修改值,使之变成值为 最短相等前后缀 长度 的函数。
那应该怎么做呢?
明确目标:
最后的next [ j ]将保存S[ j ]之前字符串的最短相等前后缀的长度(也就是最短和最长的改变)
整体思路:
从next[1]开始遍历(next [ 0 ]较为特殊,不从0开始),到下标为n(n为字符串长度)
如果next[ k ]不为0的前提下进行后续操作(如果为0,那么最短前后缀长度也为0,不用修改):
如果next [ next [ k ] ] ! = 0,那么next [ k ] = next [ next [ k ] ];
这个表示什么意思呢。意味着当前S[ k ]之前的长度为next [ k ]的前/后缀中仍有前后缀,该长度不为0。那么当前的next [ k ]的值就可以保存更小的数值了,即最短相等前后缀的长度。
这时候可能有人会问,那next [ next [ next [ k ] ] ]的值仍不为0怎么办,这不是有了更短的长度了吗?
我们要知道,这是从前往后遍历,类似于递推,当计算后面的值时,前面已经全部计算完毕。也就是说,当遍历到S [ k ]时,假设next [ k ] = m(m<k),这个时候 next [ m ]的值已经更新完毕,已经存了S[ m ]之前的最短前后缀的长度,那么直接令next [ k ]=next [ m ]即可。
这里的代码就不直接粘贴了,很短,思路里也写了。
以上就是我的全部分享
由于码龄不太长,可能会有一些考虑不周到的地方,欢迎大家批评指正。