代码随想录算法训练营第二十七天| 39 组合总和 40 组合总和II 131 分割回文串
LeetCode 39 组合总和
题目链接: 39.组合总和
思路:此题对于元素可以无限制重复选取,限制了输入不能为0,同样抽象为树形结构
回溯三部曲
- 回溯函数模板返回值及参数
本题中仍然需要全局变量result和path
vector<vector<int> > result;
vector<int> path;
void backtracking(vector<int>& candidates, int target, int sum, int startIndex)
- 回溯终止条件
此题终止条件有两种情况:一种是sum==target,另一种是sum>target
if(sum > target) {
return; //直接返回
}
if(sum == target) {
result.push_back(path);
return; //将可行路径放入result
}
- 回溯搜索遍历过程
从startIndex开始搜索candidates集合,由于可以重复选取元素,此题需要注意处理细节
for(int i = startIndex; i < candidates.size(); i++) {
sum += candidates[i];
path.push_back(candidates[i]);
backtracking(candidates, target, sum, i); //此处为i,表示可以重复取元素
sum -= candidates[i]; //回溯
path.pop_back(); //回溯
}
完整代码:
class Solution {
private:
vector<vector<int> > result;
vector<int> path;
void backtracking(vector<int>& candidates, int target, int sum, int startIndex) {
//终止条件
if(sum > target) return;
if(sum == target) {
result.push_back(path);
return;
}
//单层逻辑
for(int i = startIndex; i < candidates.size(); i++) {
sum += candidates[i];
path.push_back(candidates[i]);
backtracking(candidates, target, sum, i); //注意此处可以去重复元素,因此不需要将i加1
sum -= candidates[i]; //回溯
path.pop_back(); //回溯
}
}
public:
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
backtracking(candidates, target, 0, 0);
return result;
}
};
减枝优化
class Solution {
private:
vector<vector<int> > result;
vector<int> path;
void backtracking(vector<int>& candidates, int target, int sum, int startIndex) {
//终止条件
if(sum == target) {
result.push_back(path);
return;
}
//单层逻辑
for(int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++) {
sum += candidates[i];
path.push_back(candidates[i]);
backtracking(candidates, target, sum, i); //注意此处可以去重复元素,因此不需要将i加1
sum -= candidates[i]; //回溯
path.pop_back(); //回溯
}
}
public:
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
sort(candidates.begin(), candidates.end()); //需要先进性排序
backtracking(candidates, target, 0, 0);
return result;
}
};
注:在求和问题中,排序之后加剪枝是常见的套路!
LeetCode 40 组合总和II
题目链接: 40.组合总和II
思路:与前一道题类似,但是需要添加一个bool类型数组,用于记录同一树枝上的元素是否使用过,即在使用过程中直接进行树层去重。
整体代码为:
class Solution {
private:
vector<vector<int> > result;
vector<int> path;
void backtracking(vector<int>& candidates, int target, int sum, int startIndex, vector<bool>& used) {
if(sum == target) {
result.push_back(path);
return;
}
for(int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++) {
if(i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == false) continue;
sum += candidates[i];
path.push_back(candidates[i]);
used[i] = true;
backtracking(candidates, target, sum, i + 1, used);
used[i] = false;
sum -= candidates[i];
path.pop_back();
}
}
public:
vector<vector<int> > combinationSum2(vector<int>& candidates, int target) {
vector<bool> used(candidates.size(), false);
sort(candidates.begin(), candidates.end());
backtracking(candidates, target, 0, 0, used);
return result;
}
};
LeetCode 131 分割回文串
题目链接: 131.分割回文串
思路:切割和组合类似,同样采取树形结构的思考方式
- 递归函数及返回参数
vector<vector<string> > result;
vector<string> path;
void backtracking(const string& s, int startIndex)
- 终止条件
如果起始位置大于s的大小,则说明已经找到了一组分割方案
if(startIndex >= s.size()) {
result.push_back(path);
return;
}
- 单层搜索逻辑
for(int i = startIndex; i < s.szie(); i++) {
if(isPalindrome(s, startIndex, i)) { //如果是回文串
string str = s.substr(startIndex, i - startIndex + 1);
path.push_back(str);
}
else {
continue;
}
backtracking(s, i + 1); //寻找i+1为起始位置的子串
path.pop_back(); //回溯
}
整体代码为:
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(s, 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) {
backtracking(s, 0);
return result;
}
};
总结:本题有几个难点
- 终止条件的判断和之前不一样
- 如何模拟切割问题
- 递归的过程中如何得到回文子串
- 如何判断字符串是否回文