【问题标题】:What is the Time and Space complexity of following solution?以下解决方案的时间和空间复杂度是多少?
【发布时间】:2020-09-10 05:45:07
【问题描述】:

问题陈述: 给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,在 s 中添加空格以构造一个句子,其中每个单词都是一个有效的字典单词。返回所有可能的句子。

注意:

字典中的同一个词可能会在分词中重复使用多次。 您可以假设字典不包含重复的单词。

示例测试用例:

Input:
s = "catsanddog"
wordDict = ["cat", "cats", "and", "sand", "dog"]
Output:
[
 "cats and dog",
 "cat sand dog"
]

我的解决方案:

class Solution {
    unordered_set<string> words;
    unordered_map<string, vector<string> > memo;
public:

    vector<string> getAllSentences(string s) {
        if(s.size()==0){
            return {""};
        }
        if(memo.count(s)) {
            return memo[s];
        }
        string curWord = ""; vector<string> result;
        for(int i = 0; i < s.size(); i++ ) {
            curWord+=s[i];
            if(words.count(curWord)) {
                auto sentences = getAllSentences(s.substr(i+1));

                for(string s : sentences) {
                    string sentence = curWord + ((int)s.size()>0? ((" ") + s) : "");
                    result.push_back(sentence);
                }
            }
        }

        return memo[s] = result;
    }

    vector<string> wordBreak(string s, vector<string>& wordDict) {
        for(auto word : wordDict) {
            words.insert(word);
        }

        return getAllSentences(s);
    }
};

我不确定时间和空间的复杂性。我认为它应该是 2^n ,其中 n 是给定字符串 s 的长度。谁能帮我证明时间和空间的复杂性?

我还有以下几个问题:

  • 如果我不在 getAllSentences 函数中使用备忘录,那将是什么 这种情况下的时间复杂度?
  • 还有比这更好的解决方案吗?

【问题讨论】:

    标签: algorithm time-complexity dynamic-programming space-complexity


    【解决方案1】:

    让我们尝试逐步完成算法,但使用特定的 wordDict 来简化事情。 所以让 wordDict 是从 a 到 z 的所有字符, wordDict = ["a",..., "z"] 在这种情况下, if(words.count(curWord)) 每次 i = 0 时为真,否则为假。 另外,让我们跳过使用备忘录缓存(我们稍后会添加它)。

    在上面的例子中,我们只是递归地遍历字符串 s 直到我们到达末尾,除了结果向量之外没有任何额外的内存,它给出了以下内容: 时间复杂度为 O(n!) 空间复杂度为 O(1) - 仅存在 1 个解决方案 其中 n - s 的长度

    现在让我们来看看使用备忘录缓存如何改变我们案例中的情况。缓存将包含 n 个项目 - 我们的字符串 s 的大小,它将空间复杂度更改为 O(n)。我们的时间是一样的,因为使用备忘录缓存不会有任何命中。

    这是我们前进的基础。

    现在让我们尝试找出如果 wordDict 包含所有字母对(并且 s 的长度是 2*something,所以我们可以到达结尾),事情是如何变化的。 所以,wordDict = ['aa','ab',...,'zz'] 在这种情况下,我们继续使用 2 个字母而不是 1 个字母,其他一切都相同,这给了我们以下复杂性,而无需使用备忘录缓存: 时间复杂度为 O((n/2)!) 空间复杂度为 O(1) - 仅存在 1 个解决方案

    备忘录缓存将包含 (n/2) 个项目,给出 O(n) 的复杂度,这也将空间复杂度更改为 O(n),但所有检查的长度不同。

    现在让我们假设 wordDict 包含我们之前提到的两个字典 ('a'...'z','aa'...'zz')。

    在这种情况下,我们在不使用备忘录缓存的情况下具有以下复杂性 时间复杂度是 O((n)!) 因为我们需要检查 i=0 和 i=1 的情况,这大约使我们需要为每个步骤执行的检查次数增加了一倍,但在其他大小上,它减少了检查次数我们必须稍后再做,因为我们向前移动了 2 个字母而不是 1 个(这对我来说是最棘手的部分)。 空间复杂度为 ~O(2^n),因为每增加一个字符,结果的数量就会翻倍。 现在让我们考虑一下我们拥有的备忘录缓存。它对每 3 个字母很有用,因为例如 '...ab c...' 给出的结果与 '...a bc...' 相同,因此它在每一步将计算次数减少了 2,所以我们的复杂性如下 时间复杂度大约为 O((n/2)!),我们需要 O(2*n)=O(n) 内存来存储备忘录。我们还要记住,在 n/2 表达式中,2 反映了缓存的有效性。 空间复杂度为 O(2^n) - 2 这里是我们构建的 wordDict 的一个特征

    这些是我们了解复杂性如何根据具体情况而变化的 3 个案例。现在让我们尝试将其推广到一般情况:

    时间复杂度是 O((n/(l*e))!) 其中 l = wordDict 中单词的最小长度,e - 缓存有效性(我假设它在一般情况下为 1,但可能存在与我们在上面的案例中看到的不同的情况

    空间复杂度是O(a^n),其中a是我们wordDict中单词的相似度,可以非常非常粗略地估计为P(h/l)=(h/l)!其中 h 是字典中的最大单词长度,l 是最小单词长度(例如,如果 wordDict 包含最多 3 个字母的所有组合,这给了我们 3! 每 6 个字母的组合)

    这就是我对您的方法及其复杂性的看法。

    至于改进解决方案本身,我没有看到任何简单的改进方法。可能有另一种方法可以将字符串分成 3 个部分,然后分别处理每个部分,但如果我们可以摆脱搜索结果而只计算结果的数量而不显示它们,它肯定会起作用。

    希望对你有帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-13
      • 2017-09-23
      • 2021-02-28
      • 2019-06-02
      • 1970-01-01
      相关资源
      最近更新 更多