这里已经有两个正确的答案了,但我认为详细阐述一下可能会更加清晰。你说你想要一个9岁孩子可以理解的答案,但我觉得这不太现实(我认为没有直观的理解很容易被
误导)。也许通过阅读这个回答,能够帮助你更好地理解。
首先,外部循环明显执行
n
次,因为在循环内部未修改
i
。唯一可能多次运行的代码是块中的代码。
while (j > 0 && s[i] != s[j])
{
j = pi[j-1]
}
那么它可以运行多少次?请注意,每当满足该条件时,我们都会减少
j
的值,此时
j
最多为
pi[i-1]
。如果它达到0,则
while
循环结束。为了看清这一点的重要性,我们首先证明一个引理(你是一个非常聪明的9岁孩子):
pi[i] <= i
这是通过归纳完成的。由于在初始化
pi
时设置了
pi[0] <= 0
并且从未再次触及,因此第一步成立。然后根据归纳法,我们假设对于
0 <= a < k
,命题成立。考虑
p[k]
的值。它恰好在
pi[i] = j
的行中设置一次。那么
j
最大可以是多少呢?通过归纳,它初始化为
pi[k-1] <= k-1
。在while块中,它可能会被更新为
pi[j-1] <= j-1 < pi[k-1]
。通过另一种小型归纳,您可以看到
j
永远不会超过
pi[k-1]
。因此,在
while
循环之后,我们仍然有
j <= k-1
。最后,它可能会增加一次,这样我们就有
j <= k
,所以
pi[k] = j <= k
(这是我们需要证明的内容)。
现在回到原点,我们问“我们可以将
j
的值减少多少次”?有了引理,我们现在可以看到每次
while
循环的迭代都会单调地减少
j
的值。特别是我们有:
pi[j-1] <= j-1 < j
那么这个循环能运行多少次呢?最多是
pi[i-1]
次。敏锐的读者可能会想"你什么都没证明啊!我们知道
pi[i-1] <= i-1
但它在 while 循环内,所以它还是
O(n^2)
!" 更聪明的读者注意到了这个额外的事实:
无论我们运行 j = pi[j-1]
多少次,我们都会减少 pi[i]
的值,并缩短下一次循环的迭代!
例如,假设 j = pi[i-1] = 10
。但是在 while 循环的 ~6 次迭代之后,我们有 j = 3
,并且假设它在 s[i] == s[j]
行中增加了 1,因此 j = 4 = pi[i]
。那么在外部循环的下一次迭代中,我们从 j = 4
开始...因此我们最多只能执行 while
4 次。
谜题的最后一块是 ++j
每次循环最多运行一次。因此,我们不能在 pi
向量中有这样的东西:
0 1 2 3 4 5 1 6 1 7 1 8 1 9 1
^ ^ ^ ^ ^
Those spots might mean multiple iterations of the while loop if this
could happen
为了让这个过程更加正式,您可以先建立上述不变量,然后使用归纳法来证明
while
循环运行的总次数加上
pi[i]
的次数最多为
i
。由此可知,
while
循环运行的总次数为
O(n)
,这意味着整个外部循环的复杂度为:
O(n) // from the rest of the outer loop excluding the while loop
+ O(n) // from the while loop
=> O(n)
pi
没有被初始化! - Ed Heal