回溯+动态规划预处理
类似于多叉树的结构,每次分隔出一个字符串sub,如果sub是回文,那么继续分隔,如果sub不是回文,就把sub归还回去,当前的分割结果也要恢复到分割之前,重新分割。
class Solution {
private:
vector<vector<string>> res;
vector<string> cur;
int len;
// 回溯:寻找分割方案
void dfs(string &s, int idx, vector<vector<bool>>& dp){ // 采用引用传递,节省空间和复制时间
if(idx==len){
res.push_back(cur);
return;
}
for(int i=idx;i<len;i++){
if(dp[idx][i]){
cur.push_back(s.substr(idx, i-idx+1));
dfs(s, i+1, dp);
cur.pop_back(); // pop_back()删除vector的最后一个元素,vector长度-1
}
}
return;
}
public:
vector<vector<string>> partition(string s) {
len = s.length();
if(len==1){
res.push_back({s});
return res;
}
// 动态规划预处理寻找[j, i]区间内的回文字符串
vector<vector<bool>> dp(len, vector<bool>(len, false));
for(int i=0;i<len;i++){
for(int j=0;j<=i;j++){
if(s[i]==s[j] && (i-j<=2 || dp[j+1][i-1])){
dp[j][i] = true;
}
}
}
dfs(s, 0, dp);
return res;
}
};
回溯问题的理解:是一种借助递归的暴力搜索,判断每个可能分支,通过加入约束条件来剪枝(不满足的选择分支无需继续计算下去),可以考虑用树形结构来表示所有可能结果。
根节点:开始节点,往往是全体数据;
叶子节点:当前解空间正确的最后结果;
从根节点出发,按照要求做出各种选择,如果其中一个选择满足约束条件,继续计算下去,直到找到叶子节点,这一条从根节点到叶子节点的路径即为一个正确的解。
记忆回溯是每次计算时都记录中间结果,无论是否回溯,都可以辅助判断选择是否正确。
选择、约束条件、目标
模板:
1、for循环列举当前的所有选择;
2、判断当前选择是否正确,如果正确,在这个选择基础上继续递归,可能需要记录当前的结果substr等;如果选择不正确,递归结束;
5、撤销当前结果,判断下一个选择。