迴文分割

  週末女友不在家,打算作幾題LeetCode的題目練練手,Pick One,隨機抽中Palindrome Partitioning,題目以下:html

  Given a string s, partition s such that every substring of the partition is a palindrome.node

  Return all possible palindrome partitioning of s.算法

  For example, given s = "aab",
  Return測試

      [spa

      ["aa","b"],code

      ["a","a","b"]htm

     ]blog

  思路很簡單,構建子串的樹,用回溯法,遍歷每一個子串,若當前子串是迴文,則遞歸遍歷子節點遞歸

 

  Talk is cheap,show me the code.隊列

 1 class Solution {
 2 public:
 3     vector<vector<string>> partition(string s) {
 4         vector<vector<string>> all_answer;
 5         vector<string> answer;
 6         backtracking(s, 0, all_answer, answer);
 7         return all_answer;
 8     }
 9     void print(vector<vector<string>> all_answer)
10     {
11         for (auto it = all_answer.begin(); it != all_answer.end(); it++)
12         {
13             for (auto it2 = it->begin(); it2 != it->end(); it2++)
14             {
15                 cout << *it2 << " ";
16             }
17             cout << endl;
18         }
19     }
20 private:
21     bool is_palindrome(string s)
22     {
23         unsigned int i = 0;
24         string::iterator it = s.begin();
25         string::reverse_iterator it2 = s.rbegin();
26         while (i < s.length()/2 && it != s.end())
27         {
28             if (*it != *it2)
29             {
30                 return false;
31             }
32             ++i;
33             ++it;
34             ++it2;
35         }
36         return true;
37     }
38     void backtracking(string s, unsigned int current, vector<vector<string>> &all_answer, vector<string> &answer)
39     {
40         if (current == s.length())
41         {
42             all_answer.push_back(answer);
43             return;
44         }
45         else
46         {
47             for (unsigned int i = 1; i <= s.length()-current; ++i)
48             {
49                 string current_str = s.substr(current, i);
50                 if (is_palindrome(current_str))
51                 {
52                     answer.push_back(current_str);
53                     backtracking(s, current+i, all_answer, answer);
54                     answer.pop_back();  //backtracking
55                 }
56             }
57         }
58     }
59 };

  提交,顯示Accepted。看到問題列表中還有Palindrome Partitioning II,就想一氣呵成作完。題目以下:

  Given a string s, partition s such that every substring of the partition is a palindrome.

  Return the minimum cuts needed for a palindrome partitioning of s.

  For example, given s = "aab",
  Return 1 since the palindrome partitioning ["aa","b"] could be produced using 1 cut.

  乍一看比第一題還簡單,可是明顯測試用例會對時間複雜度作更高的要求,果真,直接改用第一題的方法,顯示Time Limit Exceeded,通不過的測試用例是:

  "fifgbeajcacehiicccfecbfhhgfiiecdcjjffbghdidbhbdbfbfjccgbbdcjheccfbhafehieabbdfeigbiaggchaeghaijfbjhi"

  這個字符串長度100,因而修改算法,觀察上一張圖,分割的次數其實就是樹的層級,以前用的回溯法是深度遍歷,若是隻須要最小的分割次數,那麼只須要按層級遍歷,找到一種分割便可返回。利用隊列實現層級遍歷,修改代碼以下:

 1 class Solution {
 2 public:
 3     int minCut_1(string s) {
 4         queue<node> node_queue;
 5         unsigned int current = 0;
 6         node first_node = {0, 0, 0};
 7         node_queue.push(first_node);
 8         while(node_queue.size())
 9         {
10             node current_check = node_queue.front();
11             node_queue.pop();
12             string current_substr = s.substr(current_check.curret_position, current_check.sub);
13             if (is_palindrome(current_substr))
14             {
15                 unsigned int new_current = current_check.curret_position+current_check.sub;
16                 if (new_current == s.length())
17                 {
18                     return current_check.layer;
19                 }
20                 else
21                 {
22                     for (unsigned int i = s.length()-new_current; i >= 1; --i)
23                     {
24                         node sub_node = {new_current, i, current_check.layer+1};
25                         node_queue.push(sub_node);
26                     }
27                 }
28             }
29         }
30         return -1;
31     }
32 
33 private:
34     struct node
35     {
36         unsigned int curret_position;
37         unsigned int sub;
38         int layer;
39     };
40     bool is_palindrome(string s)
41     {
42         unsigned int i = 0;
43         string::iterator it = s.begin();
44         string::reverse_iterator it2 = s.rbegin();
45         while (i < s.length()/2 && it != s.end())
46         {
47             if (*it != *it2)
48             {
49                 return false;
50             }
51             ++i;
52             ++it;
53             ++it2;
54         }
55         return true;
56     }
57 };

  可是這樣仍然顯示Time Limit Exceeded,由於在最壞狀況下,這個算法仍是要遍歷大多數層級,而這個測試用例就是不多回文的狀況。跑到41層的時候隊列長度就已經超過100萬了

  思考一下,咱們須要的只是最小的分割次數,可是我以前的思路老是離不開樹的遍歷,實際上是獲取了具體的分割結果,付出極大的計算代價。相似以前作過的解一道題(兩篇博客竟然相隔兩年,汗)只聚焦於分割次數,能夠改用動態規劃來作:

  令d[i]爲s[0…i]的最小分割次數,s[0…i]可分割爲s[0…j]和s[j+1…i],若s[j+1…i]是迴文,則d[i] = min(d[j]+1,d[i]).修改後代碼以下:

class Solution {
public:
    int minCut_2(string s)
    {
        vector<int> dp;
        for (unsigned int i = 1; i <= s.length();++i)
        {
            if (is_palindrome(s.substr(0, i)))
            {
                dp.push_back(0);
                continue;
            }
            else
            {
                dp.push_back(i-1);
            }

            for (unsigned int j = 1; j < i; j++)
            {
                string current_substr = s.substr(j, i-j);
                if (is_palindrome(current_substr))
                {
                    if (dp[i-1] > dp[j-1]+1)
                    {
                        dp[i-1] = dp[j-1]+1;
                    } 
                }
            }
        }
        return dp[s.length()-1];
    }
private:
    bool is_palindrome(string s)
    {
        unsigned int i = 0;
        string::iterator it = s.begin();
        string::reverse_iterator it2 = s.rbegin();
        while (i < s.length()/2 && it != s.end())
        {
            if (*it != *it2)
            {
                return false;
            }
            ++i;
            ++it;
            ++it2;
        }
        return true;
    }
};

 

  可是仍是Time Limit Exceeded,可是此次通不過的測試用例是:

   "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

  字符串長度1462,搞不定,沒思路了,看discussion裏別人的方法,原來是不只最小分割數作了動態規劃,子串的迴文判斷也用了動態規劃:

  用pal[i][j]記錄子串s[i..j]是不是迴文,根據pal[j+1][i-1]與s[i]==s[j]判斷pal[j][i]是不是迴文。修改代碼以下:

class Solution {
public:
    int minCut(string s) {
        if(s.empty()) return 0;
        int n = s.length();
        vector<vector<bool>> pal(n,vector<bool>(n,false));
        vector<int> dp(n);
        for (int i = 0; i < n;++i)
        {
            dp[i] = i>1 ? dp[i-1]+1 : i;
            pal[i][i] = true;
            for (int j = 0; j < i; j++)
            {
                if(s[i]==s[j] && (i-j<2 || pal[j+1][i-1]))
                {
                    pal[j][i]=true;
                    if (0 == j)
                    {
                        dp[i] = 0;
                    }
                    else if (dp[j-1]+1 < dp[i])
                    {
                        dp[i] = dp[j-1]+1;
                    }
                }
            }
        }
        return dp[n-1];
    }
};

 

  提交後終於Accepted,耗時244ms。

  作這題花費了一天時間,把樹的遍歷和動態規劃也都再複習了一遍,獲益匪淺。

相關文章
相關標籤/搜索