找到 LPS最长前缀也是后缀数组的算法复杂度是 O(n) 吗?

问题描述

以下是我的算法实现,用于查找 LPS 数组,这是 KMP 算法的一部分。

public static int[] getLps(String needle)
{
    int[] lps = new int[needle.length()];
    int j = 0;
    lps[j] = 0;

    for (int i=1; i < needle.length(); i++)
    {
        if (needle.charat(j) == needle.charat(i))
        {
            j++;
            lps[i] = j;
        }
        else
        {
            while (j != 0)
            {
                j = lps[j-1];

                if (needle.charat(j) == needle.charat(i))
                {
                    j++;
                    lps[i] = j;
                    break;
                }
            }
        }
    }

    return lps;
}

在我跟随的解释 KMP 算法的文章中,提到找出 LPS 数组的逻辑复杂度是 O(n),这让我有点困惑。正如您在上面的代码中看到的,for 循环中有一个内部的 while 循环。在最坏的情况下,内部 while 将运行 j 次迭代。这不应该导致我们的复杂度大于 O(n) 吗?

我认为证明 O(n) 复杂性的一种方法是,在内部 while 循环中,我们没有重复整个逻辑。我们只是减少 j 的值,直到它达到 0 或与第 i 个索引处的值匹配。所以这个while循环被认为是来自外部for循环的单次迭代的一部分,最终我们算法的复杂度变成了O(n)。

任何人都可以确认这一点或对此提供更多说明吗?

解决方法

什么是j?这是当前前缀的长度。

在每一步我们都将后缀加长一个,我们可能会得到重合前缀加长一个。但前缀长度可能会变小,有时为零。但是如果我们让前缀长度为零,并且将它一个字符一个一个地扩展,我们必须执行很多操作。相反,该算法使用智能优化 - 前缀长度减少 1 以重复使用已计算的信息。

最重要的时刻 - 前缀减少的总数不能超过字符串长度 - 这就是为什么复杂度是线性的。