题目描述:
题号:131
给你一个字符串 s
,请你将 s
分割成一些子串,使每个子串都是 回文串 。返回 s
所有可能的分割方案。
解题思路:
思路一:回溯 + 动态规划
这道题可以通过回溯法来解决。首先,我们需要一个辅助函数来判断一个字符串是否是回文串。然后,使用回溯法从字符串的每个位置开始尝试分割,如果该位置分割后的子串是回文串,则递归处理剩余部分。
具体步骤如下:
-
定义
is_palindrome
函数,用于判断一个字符串是否是回文串。 -
定义
partition
函数,使用回溯法搜索所有可能的分割方案。从字符串的每个位置开始尝试分割,如果该位置到某个位置之间的子串是回文串,则将其加入当前路径,并递归处理剩余部分。当遍历完整个字符串时,将当前路径加入结果集。 -
调用
partition
函数并格式化输出结果,即将每个分割方案中的子串列表转换为字符串列表。
时间复杂度:O(N * 2^N)
空间复杂度:O(N * N)
C++
// C++
class Solution {
bool isPalindrome(string& s, int left, int right) {
while (left < right) {
if (s[left++] != s[right--]) {
return false;
}
}
return true;
}
public:
vector<vector<string>> partition(string s) {
int n = s.length();
vector<vector<string>> ans;
vector<string> path;
// start 表示当前这段回文子串的开始位置
auto dfs = [&](auto&& dfs, int i, int start) {
if (i == n) {
ans.emplace_back(path);
return;
}
// 不选 i 和 i+1 之间的逗号(i=n-1 时一定要选)
if (i < n - 1) {
dfs(dfs, i + 1, start);
}
// 选 i 和 i+1 之间的逗号(把 s[i] 作为子串的最后一个字符)
if (isPalindrome(s, start, i)) {
path.push_back(s.substr(start, i - start + 1));
dfs(dfs, i + 1, i + 1); // 下一个子串从 i+1 开始
path.pop_back(); // 恢复现场
}
};
dfs(dfs, 0, 0);
return ans;
}
};
go
// go
func isPalindrome(s string, left, right int) bool {
for left < right {
if s[left] != s[right] {
return false
}
left++
right--
}
return true
}
func partition(s string) (ans [][]string) {
n := len(s)
path := []string{}
// start 表示当前这段回文子串的开始位置
var dfs func(int, int)
dfs = func(i, start int) {
if i == n {
ans = append(ans, append([]string(nil), path...)) // 复制 path
return
}
// 不选 i 和 i+1 之间的逗号(i=n-1 时一定要选)
if i < n-1 {
dfs(i+1, start)
}
// 选 i 和 i+1 之间的逗号(把 s[i] 作为子串的最后一个字符)
if isPalindrome(s, start, i) {
path = append(path, s[start:i+1])
dfs(i+1, i+1) // 下一个子串从 i+1 开始
path = path[:len(path)-1] // 恢复现场
}
}
dfs(0, 0)
return
}