这里已经有两个答案是正确的,但我经常认为一个完整的答案
证明可以使事情更清楚。你说你想要一个 9 岁孩子的答案,但是
我不认为这是可行的(我认为很容易被愚弄以为这是真的
实际上没有任何直觉知道为什么它是真的)。也许解决这个答案会有所帮助。
首先,外部循环运行n 次很明显,因为i 没有被修改
循环内。循环中唯一可以运行多次的代码是
块
while (j > 0 && s[i] != s[j])
{
j = pi[j-1]
}
那么它可以运行多少次?请注意,每次条件都是
满意我们减少j 的值,此时最多为
pi[i-1]。如果它达到 0,则 while 循环完成。要了解为什么这很重要,
我们首先证明一个引理(你是一个非常聪明的 9 岁孩子):
pi[i] <= i
这是通过归纳来完成的。 pi[0] <= 0 因为它在 pi 的初始化中设置过一次,并且再也没有被触及过。然后归纳地让0 < k < n 并假设
该声明适用于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 循环运行的total 次是O(n),这意味着整个外部循环具有复杂性:
O(n) // from the rest of the outer loop excluding the while loop
+ O(n) // from the while loop
=> O(n)