实际上,分割子集问题也是组合问题
(图源代码随想录网站)
一个套路,也就是说,每次递归函数参数列表传入start
的时候,选中的元素相当于是在最后面划了一条分割线
回文子串的判断剪枝操作就很简单了,类似于左右指针处理一下就行
class Solution {
public:
vector<vector<string>> res;
vector<string> resVec;
void resInit();
bool isValid(string str){
int left = 0;
int right = str.size()-1;
while(left <= right){
if(str[left] == str[right]){
left++;
right--;
}else{
return false;
}
}
return true;
}
void traceback(vector<string> resVec,string & s,int start){
if(start >= s.size()){
res.push_back(resVec);
return;
}
for(int i = start;i< s.size();i++){
string chooseStr = string(s.begin()+start,s.begin()+i+1);
if(!isValid(chooseStr)){
continue;
}
resVec.push_back(chooseStr);
traceback(resVec,s,i+1);
resVec.pop_back();
}
}
vector<vector<string>> partition(string s) {
traceback(resVec,s,0);
return res;
}
};
最近有个疑惑,假如都是使用一个vector来获取递归路径,但为啥像是131这种传统的组合/子集递归的终止条件里去将当前路径加入结果集res就没问题,而二叉树的前序遍历递归获取已走路径时候就会获取两次当前路径呢?
附一下代码:
vector<vector<int>> res;
vector<int> resVec;
void tracebackTree(vector<int> resVec,TreeNode * node){
if(node ==NULL){
//路径重复加入!!!
res.push_back(resVec);
return;
}
//做选择->撤销选择 对应131代码的for循环的每轮次的循环处理
resVec.push_back(node->val);
traceback(resVec,node->left);
resVec.pop_back();
//做选择->撤销选择 对应131代码的for循环的每轮次的循环处理
resVec.push_back(node->val);
traceback(resVec,node->right);
resVec.pop_back();
}
两段代码对比一下,会发现131那个代码的结构和这段二叉树获取前序路径的代码结构基本一样的逻辑,但是这段二叉树获取前序路径的代码如果一定要在终止条件把当前路径resVec加入结果集的话,就会面临两次重复加入的情况
为什么呢,二者有什么差别呢?
我们考虑每次的终止条件都是刚好做出当前节点下的选择以后,恰好结束的时刻,131的问题下
,刚好做出当前节点下的选择以后,只有一种情况可以通向恰好结束的时刻;而二叉树
由于分为左子树和右子树遍历,刚好做出当前节点下的选择以后,有两种情况可以通向恰好结束的时刻,所以会出现重复加入结果集的问题