【问题标题】:Code complexity of longest non-repeating substring algorithm最长不重复子串算法的代码复杂度
【发布时间】:2016-12-14 21:18:28
【问题描述】:

问题:

给定一个字符串,找出最长的不重复字符的子字符串的长度。

例子:

给定“abcabcbb”,答案是“abc”,长度为3。

解决方案:

int lengthOfLongestSubstring(char* s) {
    if (s == NULL) {
        return 0;
    }
    myHash  hash;
    int start = 0;
    int end = 0;
    int maxLen = 0;
    while (s[end] != '\0') {
        char c = s[end];
        if (hash.find(c) == hash.end()) {
            hash[c] = end;
            end++;
        }else {
            if (end - start > maxLen) {
                maxLen = end - start;
            }
            int index = hash[c];
            while(start <= index) {
                hash.erase(s[start]);
                start++;
            }
        }
    }
    if (end - start > maxLen) {
        maxLen = end - start;
    }
    return maxLen;
}

复杂性:

有人说这个算法复杂度是O(n),但我认为它没有考虑第二个循环,它小于O(n^2)但应该大于O(n)。

我们应该如何分析最坏情况的复杂性?

【问题讨论】:

  • 这不是有效的 C 代码。 hash.find 要求 myHash 是结构类型;但hash[c] 要求myHash 是数组或指针类型。你是不是想把它标记为 C++?
  • abc 不是唯一的答案,你还有 bca 和 cab。

标签: c algorithm time-complexity


【解决方案1】:

渐近复杂度分析比只计算循环更复杂。

然而,在我们进一步讨论之前,请注意,如果一个算法的渐近复杂度为 O(n),那么它也是 O(n2),如后者表示较弱的界限。大概您正在寻找最紧密的界限。

现在让我们看看在外循环的所有迭代中执行的所有操作的总和。在每次迭代中,都会计算 hash.find(c) == hash.end()。我们假设每个这样的评估成本 O(1)。那么有两种选择:

  1. 当前字符 s[end] 与当前子字符串中的所有其他字符不同,由哈希查找判断。在这种情况下,当前字符被添加到当前子字符串中,end 递增。

  2. 当前字符复制了当前子字符串中的前一个字符。在这种情况下,当前字符的先前出现以及当前子字符串中的每个前面的字符都将从子字符串中删除。

原始字符串中的每个n 字符都被添加到当前字符串一次,并且最多从中删除一次,并且每个单独字符的添加或删除所涉及的操作花费O(1)。在外循环中执行的剩余操作每次迭代花费 O(1)。循环的每次迭代都执行一次添加或至少一次删除,因此总共不能超过 2*n 次迭代。这使得循环嵌套的最坏情况成本为n * O(1) [用于添加] + n * O(1) [用于移除] + 2 * n * O(1) [用于其他操作],总成本以4 * n * O(1) = O(4n) = O(n) 为界。

【讨论】:

    【解决方案2】:

    这个算法的复杂度确实是O(n)。

    证明:考虑startend 的值。外循环的每次迭代都会执行以下两个动作之一:

    • 提前end 1,或
    • 在循环中推进start 一次或多次。

    end 前进的次数等于输入的长度nstart 前进的次不能超过n,因为index 永远不会设置为大于n 的数字,并且start 永远不会递减。

    因此,外循环运行的总次数为n,内循环运行的总次数为n,时间复杂度为O(n)。

    请注意,内部循环不会一次完成所有n 迭代。它的迭代在外循环的迭代中“分布”。但是,如果将内循环的迭代次数加起来,在外循环的所有迭代中,它们不会超过 n

    【讨论】:

      【解决方案3】:

      如果我理解您正确使用的算法和数据结构,请考虑最坏的情况:我们一遍又一遍地得到相同字符的字符串,并且每次都必须擦除。在这种情况下,我们对字符串执行了大约2n 操作。这仍然是线性增长,前面有一个常数。所以,为了理解它是如何增长的,我们仍然是线性增长——但是,正如你所说,在nn^2 之间,因为我们大约是2n 最坏的情况。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-02-16
        • 2021-12-26
        • 1970-01-01
        • 1970-01-01
        • 2021-08-29
        • 2016-12-05
        • 2013-05-28
        • 2017-05-22
        相关资源
        最近更新 更多