Algorithm Z算法背后的直觉
Z算法是一种具有O(n)复杂度的字符串匹配算法 一个用例是从字符串B中查找字符串A的最长出现时间。例如,从Algorithm Z算法背后的直觉,algorithm,Algorithm,Z算法是一种具有O(n)复杂度的字符串匹配算法 一个用例是从字符串B中查找字符串A的最长出现时间。例如,从“stackoverflow”中查找“overdose”的最长出现时间将是“over”。您可以通过使用组合字符串调用Z算法来发现这一点。“overdose#stackoverflow”(其中#是两个字符串中都不存在的字符)。然后,Z算法将尝试将组合字符串与自身匹配,并创建一个数组Z[],其中Z[i]给出从索引i开始的最长匹配长度。在我们的例子中: index 0 1 2 3 4
“stackoverflow”
中查找“overdose”
的最长出现时间将是“over”
。您可以通过使用组合字符串调用Z算法来发现这一点。“overdose#stackoverflow”
(其中#是两个字符串中都不存在的字符)。然后,Z算法将尝试将组合字符串与自身匹配,并创建一个数组Z[],其中Z[i]给出从索引i开始的最长匹配长度。在我们的例子中:
index 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
string o v e r d o s e # s t a c k o v e r f l o w
z (21) 0 0 0 0 1 0 0 0 0 0 0 0 0 4 0 0 0 0 0 1 0
该算法有很多代码实现和面向数学的解释,以下是一些很好的解释:
我知道它是怎么工作的,但我不明白为什么。这几乎像是黑魔法。我有一个非常强烈的直觉,这项任务应该采取
O(n^2)
,但这里有一个算法,它在O(n)
中完成,我也不觉得它完全直观,所以我认为我有资格回答。否则我只能说你不明白,因为你是个白痴,这肯定不是你所希望的答案:-)
举例说明(从解释中引用):
所以,让我们试着更加直观
首先,我猜想O(n^2)的常见直觉是:对于长度为n的字符串,如果在没有其他信息的情况下,将其放置在字符串中的随机位置I,则必须匹配x(这就是我对它的理解,希望这能有所帮助。我想对这个算法有更深入的理解,因此我发现了这个问题 起初我不理解,但后来我发现这足以让我理解,我注意到这篇文章并不完全准确,而且它省略了思考过程中的一些步骤,让人有点困惑 让我试着纠正那篇文章中的错误,并澄清一些我认为可以帮助人们将点与线联系起来的步骤。在这个过程中,我希望我们能从原作者那里学到一些直觉。在解释中,我将混合一些引用自codeforces的块和我自己的注释,这样我们就可以将原始帖子与我们的讨论保持接近 Z算法开始时为: 当我们迭代字符串中的字母时(索引i从1到n - 1) ,我们保持一个间隔[L, R] 这是最大R的间隔,使得1 ≤ L ≤ 我 ≤ R和S[L…R]是一个前缀子字符串(如果不存在这样的间隔,就让L = R = - 1). 因为我 = 1,我们可以通过比较S[0…]和S[1…]来简单地计算L和R。此外,在这个过程中,我们也得到了Z 这是简单明了的 现在假设我们有正确的间隔[L, R] 因为我 - 1和i之前的所有Z值 - 1.我们将计算Z[i]和新的[L, R] 通过以下步骤:
- 如果我 > R、 那么就不存在在i之前开始、在i或之后结束的S前缀子串。如果存在这样的子串,[L, R] 将是该子字符串的间隔,而不是其当前值。因此我们“重置”并计算一个新的[L, R] 通过比较S[0…]和S[i…],同时得到Z[i](Z[i] = R - L + 1)
- 否则,我 ≤ R、 所以现在的[L, R] 至少延伸到i。让k = 我 - L.我们知道Z[i] ≥ min(Z[k], R - 我 + 1) 因为S[i..]与S[k..]至少匹配R - 我 + 1个字符(它们位于[L, R] 我们知道是前缀子字符串的间隔)。现在我们有更多的案例要考虑。
- 基于区间[L,R]和i的定义 ≤ R、 我们已经确认S[0…R-L]==S[L…R],因此S[0…k]==S[L…i]和S[k…R-L]==S[i…R]李>
- 假设Z[k]=x,根据Z的定义,我们知道S[0…x]==S[k…k+x]李>
- 结合以上方程,我们知道当x
Correctness is inherent in the algorithm and is pretty intuitively clear.