求 字符串 最短相等前后缀 长度(时间复杂度O(n))

153 阅读3分钟
  • 相信已经提出这个问题的小伙伴已经对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 ]即可。

这里的代码就不直接粘贴了,很短,思路里也写了。

以上就是我的全部分享

由于码龄不太长,可能会有一些考虑不周到的地方,欢迎大家批评指正。