Day27 回溯 LeetCode 39 40 131

70 阅读1分钟

39. 组合总和

心得

  • 回溯

题解

  • 注意回溯调用时的I,需要先排序,同时做好剪枝
class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(const vector<int>& candidates, int target, int sum, int startIndex) {
        if (sum == target) {
            result.push_back(path);
            return;
        }
        // 求和剪枝,与其下一层大于返回不如让其直接小于target
        for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++) {
            sum += candidates[i];
            path.push_back(candidates[i]);
            backtracing(candidates, target, sum, i); // 此处i表示可以取重复
            sum -= candidates[i];
            path.pop_back();
        }
    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        result.clear();
        path.clear();
        if (candidates.size() == 0) return result;
        sort(candidates.begin(), candidates.end()); // 需要排序,便于剪枝,大于即不进入下一层循环
        backtracing(candidates, target, 0, 0);
        return result;
    }
};

40. 组合总和 II

心得

  • 未做好去重

题解

  • 相比上题,新增去重操作,好好理解candidate[i] 和candidate[i-1]的去重逻辑
  • 可以有连续的相同重复元素,但是不能有单个相同元素选一然后和其他组合的情况,类似连续1 1 1 1 2 4 的情况,可以一直连续,但是不能存在从多个1中选子集(该操作选完后剩下的也可以选类似操作造成重复)
class Solution {
public:
    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++) {
            if (i > startIndex && candidates[i] == candidates[i-1]) { // 去重的逻辑
                continue;
            }
            sum += candidates[i];
            path.push_back(candidates[i]);
            backtracking(candidates, target, sum, i + 1);
            sum -= candidates[i];
            path.pop_back();
        }
    }
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        result.clear();
        path.clear();
        if (candidates.size() == 0) return result;
        sort(candidates.begin(), candidates.end());
        backtracking(candidates, target, 0, 0);
        return result;
    }
};

131. 分割回文串

心得

  • 没有思路

题解

  • 模拟组合问题,画出树图,然后画线来类比组合问题
  • 注意如何判断回文,如何截取子串,已经startIndex的应用场景,终止条件为遍历结束等
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);
        }
        for (int i = startIndex; i < s.size(); i++) {
						// [startIndex, i] 即为子串
            if (isPalindrome(s, startIndex, i)) {
                path.push_back(s.substr(startIndex, i - startIndex + 1));
            } else {
                continue;
            }
            backtracking(s, i + 1); // 当前已经是回文,下一个开始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) {
        result.clear();
        path.clear();
        if (s.size() == 0) return result;
        backtracking(s, 0);
        return result;
    }
};