Palindrome分区算法的时间复杂度是多少?

时间:2014-07-06 00:14:48

标签: c++ algorithm big-o dynamic-programming palindrome

回文分区

  

给定一个字符串s,分区s使得每个子字符串   分区是回文。
返回所有可能的回文   划分s。

我个人认为,时间复杂度为O(n ^ n),n是给定字符串的长度。

  

谢谢 Dan Roche ,时间紧迫度= O(n *(2 ^ n)),请查看以下详细信息。

#include <vector>
using namespace std;

class Solution {
public:
vector<vector<string>> partition(string s) {
    vector<vector<string>> list;
    vector<string> subList;

    // Input validation.
    if (s.length() <= 1) {
        subList.push_back(s);
        list.push_back(subList);
        return list;
    }

    int len = s.length();
    vector<vector<bool>> memo(len, vector<bool>(len));
    for (int i = 0; i < len; i ++) {
        for (int j = 0; j < len; j ++) {
            if (i >= j) memo[i][j] = true;
            else memo[i][j] = false;
        }
    }

    int start = 0;
    helper(s, start, list, subList, memo);

    return list;
}

void helper(string s, int start, 
            vector<vector<string>> &list, vector<string> &subList,
            vector<vector<bool>> &memo) {

    // Base case.
    if (start > s.length() - 1) {
        vector<string> one_rest(subList);
        list.push_back(one_rest);
        return;
    }

    for (int len = 1; start + len <= s.length(); len ++) {
        int end = start + len - 1;

        memo[start][end] = (len == 1) ||
                           (memo[start + 1][end - 1] && s[start] == s[end]);

        if (memo[start][end] == true) {
            // Have a try.
            subList.push_back(s.substr(start, len));

            // Do recursion.
            helper(s, end + 1, list, subList, memo);

            // Roll back.
            subList.pop_back();
        }
    }
}
};

2 个答案:

答案 0 :(得分:3)

最坏情况下的运行时间是O(n * 2 ^ n)。这当然是你所怀疑的指数,但不如O(n ^ n)那么糟糕。

这是我如何得到O(n * 2 ^ n):你的顶级函数有一个O(n ^ 2)循环来初始化备忘录,然后在整个字符串上调用helper。因此,如果我们编写H(n)来调用助手(s.length()-start)等于n的成本,那么算法的总成本将是

  

成本= H(n)+ O(n ^ 2)

H(n)的基本情况是s.length() - start等于1,然后只是复制列表的成本:

  

H(1)= O(n)

对于递归情况,如果if条件memo[start][end]每次都是true,则会在大小(n-1)上进行(n-1)次递归调用,( n-2),(n-3),...,2,1。除了对helper的这些递归调用之外,还必须调用相同大小的substr函数,费用总计为O(n ^ 2)。因此总体而言,对于n> 1,H(n)的成本是

  

H(n)= H(n-1)+ H(n-2)+ ... + H(1)+ O(n ^ 2)

(我会把它写成总和但是没有LaTeX支持。)

现在您可以为H(n-1)编写相同的表达式,然后替换为简化:

  

H(n)= 2 H(n-1)+ O(n)

这解决了

  

H(n)= O(n * 2 ^ n)

由于它大于O(n ^ 2),整个成本也是O(n * 2 ^ n)。


注意:您可以通过在单个O(n ^ 3)循环中预先计算所有子字符串来稍微改善这一点。你也可以为memo数组做同样的事情。但是,这并没有改变渐近的大O界限。

实际上,O(n * 2 ^ n)是最优的,因为在最坏的情况下,字符串是n次重复相同的字符,如“aaaaaa”,在这种情况下,有2 ^ n个可能的分区,每个都具有大小为n,总输出大小为Ω(n * 2 ^ n)。

答案 1 :(得分:2)

应为O(n * 2 ^ n)。你基本上是在尝试每个可能的分区。对于长度为n的字符串,您将有2 ^(n - 1)种方法对其进行分区。这是因为,分区相当于放置&#34; |&#34;在b / t两个字符。有n - 1个这样的插槽来放置&#34; |&#34;。每个插槽只有两个选择 - 放置&#34; |&#34;或不放置&#34; |&#34;。因此,2 ^(n - 1)种方式放置&#34; |&#34; s。

然后,对于每个唯一分区,您必须遍历整个字符串(在最糟糕的情况下,当您有重复的字符时)以确保每个分区都是回文。所以n * 2 ^(n - 1)= O(n * 2 ^ n)。