週末女友不在家,打算作幾題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。
作這題花費了一天時間,把樹的遍歷和動態規劃也都再複習了一遍,獲益匪淺。