问题描述
给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串。返回 s 所有可能的分割方案。
示例 1:
输入:s = “aab”
输出:[[“a”,“a”,“b”],[“aa”,“b”]]
示例 2:
输入:s = “a”
输出:[[“a”]]
提示:
1 <= s.length <= 16
s 仅由小写英文字母组成
回溯
核心思路
本题这涉及到两个关键问题:
- 切割问题,有不同的切割方式
- 判断回文
切割问题类似组合问题,也可以抽象为一棵树形结构。递归用来纵向遍历,for循环用来横向遍历,切割线(startIndex)切割到字符串的结尾位置,说明找到了一个切割方法。
判断一个字符串是否是回文,可以使用双指针法,一个指针从前向后,一个指针从后向前,如果前后指针所指向的元素是相等的,就是回文字符串了。
实现要点
注意切割过的位置,不能重复切割,所以,backtracking(s, i + 1); 传入下一层的起始位置为i + 1。
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| class Solution { private: vector<vector<string>> result; vector<string> path; void backtracking (const string& s, int startIndex) { if (startIndex >= s.size()) { result.push_back(path); return; } for (int i = startIndex; i < s.size(); i++) { if (isPalindrome(s, startIndex, i)) { string str = s.substr(startIndex, i - startIndex + 1); path.push_back(str); } else { continue; } backtracking(s, i + 1); path.pop_back(); } } bool isPalindrome(const string& s, int start, int end) { for (int i = start, j = end; i < j; i++, j--) { if (s[i] != s[j]) { return false; } } return true; } public: vector<vector<string>> partition(string s) { result.clear(); path.clear(); backtracking(s, 0); return result; } };
|
- 时间复杂度: O(n∗2n)
- 空间复杂度: O(n)
优化
核心思路
如何更高效的计算一个子字符串是否是回文字串?
给定一个字符串s, 长度为n, 它成为回文字串的充分必要条件是s[0] == s[n-1]且s[1:n-1]是回文字串。
我们可以高效地事先一次性计算出, 针对一个字符串s, 它的任何子串是否是回文字串, 然后在我们的回溯函数中直接查询即可, 省去了双指针移动判定这一步骤。
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| class Solution { private: vector<vector<string>> result; vector<string> path; vector<vector<bool>> isPalindrome; void backtracking (const string& s, int startIndex) { if (startIndex >= s.size()) { result.push_back(path); return; } for (int i = startIndex; i < s.size(); i++) { if (isPalindrome[startIndex][i]) { string str = s.substr(startIndex, i - startIndex + 1); path.push_back(str); } else { continue; } backtracking(s, i + 1); path.pop_back(); } } void computePalindrome(const string& s) { isPalindrome.resize(s.size(), vector<bool>(s.size(), false)); for (int i = s.size() - 1; i >= 0; i--) { for (int j = i; j < s.size(); j++) { if (j == i) {isPalindrome[i][j] = true;} else if (j - i == 1) {isPalindrome[i][j] = (s[i] == s[j]);} else {isPalindrome[i][j] = (s[i] == s[j] && isPalindrome[i+1][j-1]);} } } } public: vector<vector<string>> partition(string s) { result.clear(); path.clear(); computePalindrome(s); backtracking(s, 0); return result; } };
|