当前位置:   article > 正文

算法刷题day20|回溯:39. 组合总和、40. 组合总和 II、131. 分割回文串

算法刷题day20|回溯:39. 组合总和、40. 组合总和 II、131. 分割回文串

39. 组合总和

回溯

  1. class Solution {
  2. private:
  3. vector<vector<int>> result;
  4. vector<int> path;
  5. void backtracking(vector<int>& candidates, int target, int sum, int startIndex) {
  6. if (sum > target) {
  7. return;
  8. }
  9. if (sum == target) {
  10. result.push_back(path);
  11. return;
  12. }
  13. for (int i = startIndex; i < candidates.size(); i++) {
  14. sum += candidates[i];
  15. path.push_back(candidates[i]);
  16. backtracking(candidates, target, sum, i); // 不用i+1了,表示可以重复读取当前的数
  17. sum -= candidates[i];
  18. path.pop_back();
  19. }
  20. }
  21. public:
  22. vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
  23. result.clear();
  24. path.clear();
  25. backtracking(candidates, target, 0, 0);
  26. return result;
  27. }
  28. };

剪枝优化

对总集合排序之后,如果下一层的sum(就是本层的 sum + candidates[i])已经大于target,就可以结束本轮for循环的遍历

  1. // 如果 sum + candidates[i] > target 就终止遍历
  2. for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++) {
  3. sum += candidates[i];
  4. path.push_back(candidates[i]);
  5. backtracking(candidates, target, sum, i);
  6. sum -= candidates[i];
  7. path.pop_back();
  8. }

40. 组合总和 II

回溯(used数组版)

我们要去重的是同一树层上的“使用过”,同一树枝上的都是一个组合里的元素,不用去重

例如,candidates = [1, 1, 2], target = 3,遍历第一个 1 时,会取到[1, 2],遍历到第二个 1 时,也会取到[1, 2],此时就要对同一树层上的相同的值去重

  1. class Solution {
  2. private:
  3. vector<vector<int>> result;
  4. vector<int> path;
  5. void backtracking(vector<int>& candidates, int target, int sum, int startIndex, vector<bool>& used) {
  6. if (sum == target) {
  7. result.push_back(path);
  8. return;
  9. }
  10. for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++) {
  11. // used[i - 1] == true,说明同一树枝candidates[i - 1]使用过
  12. // used[i - 1] == false,说明同一树层candidates[i - 1]使用过
  13. // 要对同一树层使用过的元素进行跳过
  14. if (i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == false) {
  15. continue;
  16. }
  17. sum += candidates[i];
  18. path.push_back(candidates[i]);
  19. used[i] = true;
  20. backtracking(candidates, target, sum, i + 1, used); // 和39.组合总和的区别1,这里是i+1,每个数字在每个组合中只能使用一次
  21. used[i] = false;
  22. sum -= candidates[i];
  23. path.pop_back();
  24. }
  25. }
  26. public:
  27. vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
  28. vector<bool> used(candidates.size(), false);
  29. path.clear();
  30. result.clear();
  31. // 首先把给candidates排序,让其相同的元素都挨在一起。
  32. sort(candidates.begin(), candidates.end());
  33. backtracking(candidates, target, 0, 0, used);
  34. return result;
  35. }
  36. };

 回溯(不用used数组版)

  1. for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++) {
  2. // 要对同一树层使用过的元素进行跳过
  3. if (i > startIndex && candidates[i] == candidates[i - 1]) {
  4. continue;
  5. }
  6. sum += candidates[i];
  7. path.push_back(candidates[i]);
  8. backtracking(candidates, target, sum, i + 1); // 和39.组合总和的区别1,这里是i+1,每个数字在每个组合中只能使用一次
  9. sum -= candidates[i];
  10. path.pop_back();
  11. }

131. 分割回文串

回溯版一

  1. class Solution {
  2. public:
  3. bool isprim(const string& s){
  4. string str = s;
  5. reverse_copy(s.begin(), s.end(), str.begin());
  6. /*
  7. string str(s.rbegin(), s.rend());
  8. */
  9. if (str == s){
  10. return true;
  11. }
  12. return false;
  13. }
  14. vector<vector<string>> result;
  15. vector<string> path;
  16. void backtracking(const string& s, int startIndex){
  17. if (startIndex >= s.size()){
  18. result.push_back(path);
  19. return;
  20. }
  21. for (int i = startIndex; i < s.size(); i++){
  22. string flag = s.substr(startIndex, i - startIndex + 1);
  23. if (isprim(flag) == true){
  24. path.push_back(flag);
  25. }else{
  26. continue;
  27. }
  28. backtracking(s, i + 1);
  29. path.pop_back();
  30. }
  31. }
  32. vector<vector<string>> partition(string s) {
  33. backtracking(s, 0);
  34. return result;
  35. }
  36. };

reverse_copy用法:

template< class BidirIt, class OutputIt > OutputIt reverse_copy( BidirIt first, BidirIt last, OutputIt d_first ); 

first, last-要复制的元素范围
d_first-新范围的起始

 注:要将翻转值写进去的string必须有一定的长度

C++ 容器中 begin()、cbegin()、rbegin()、crbegin

  • begin();end()正序迭代器
  • cbegin();cend() 返回 const 的begin();end()
  • rbegin();rend() 逆序迭代器
  • crbegin();crend() 返回 const 的 rbegin();rend()

回溯版二

主要是判断回文串的方法不同

  1. class Solution {
  2. private:
  3. vector<vector<string>> result;
  4. vector<string> path; // 放已经回文的子串
  5. void backtracking (const string& s, int startIndex) {
  6. // 如果起始位置已经大于s的大小,说明已经找到了一组分割方案了
  7. if (startIndex >= s.size()) {
  8. result.push_back(path);
  9. return;
  10. }
  11. for (int i = startIndex; i < s.size(); i++) {
  12. if (isPalindrome(s, startIndex, i)) { // 是回文子串
  13. // 获取[startIndex,i]在s中的子串
  14. string str = s.substr(startIndex, i - startIndex + 1);
  15. path.push_back(str);
  16. } else { // 不是回文,跳过
  17. continue;
  18. }
  19. backtracking(s, i + 1); // 寻找i+1为起始位置的子串
  20. path.pop_back(); // 回溯过程,弹出本次已经添加的子串
  21. }
  22. }
  23. bool isPalindrome(const string& s, int start, int end) {
  24. for (int i = start, j = end; i < j; i++, j--) {
  25. if (s[i] != s[j]) {
  26. return false;
  27. }
  28. }
  29. return true;
  30. }
  31. public:
  32. vector<vector<string>> partition(string s) {
  33. result.clear();
  34. path.clear();
  35. backtracking(s, 0);
  36. return result;
  37. }
  38. };

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Li_阴宅/article/detail/906230
推荐阅读
相关标签
  

闽ICP备14008679号