算法训练1-day20-回溯

38 阅读2分钟

回溯法来了

  1. 39. 组合总和

先排序,然后就可以做剪枝

AC代码:

class Solution {
public:
    vector<vector<int>> ans;
    vector<int> path;
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        sort(candidates.begin(), candidates.end());
        backtracing(candidates, target, 0);
        return ans;
    }

    void backtracing(vector<int>& candidates, int target, int startIndex) {
        if (target == 0) {
            ans.push_back(path);
            return;
        }
        if (startIndex >= candidates.size()) return;

        for (int i = startIndex; i < candidates.size(); ++i) {
            // 先对candidates排序,保证是升序排列的
            if (target - candidates[i] < 0) break;
            path.push_back(candidates[i]);
            backtracing(candidates, target - candidates[i], i);
            path.pop_back();
        }
    }
};
  1. 40. 组合总和 II

要处理重复值,不能有重复的组合,也就是对应于遍历的每一层,对应于每一个startIndex的for循环,我们不能选取重复值,要做到去重,我们可以先排序,然后再判断后面的元素是否已经选过了

AC代码:

class Solution {
public:
    vector<vector<int>> ans;
    vector<int> path;
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        sort(candidates.begin(), candidates.end());
        backtracing(candidates, target, 0);
        return ans;
    }

    void backtracing(vector<int>& candidates, int target, int startIndex) {
        if (target == 0) {
            ans.push_back(path);
            return;
        }

        for (int i = startIndex; i < candidates.size(); ++i) {
            // 剪枝
            if (target - candidates[i] < 0) break;
            // 去重,因为我们已经排好序了
            if (i > startIndex && candidates[i - 1] == candidates[i]) continue;
            path.push_back(candidates[i]);
            backtracing(candidates, target - candidates[i], i + 1);
            path.pop_back();
        }
    }
};
  1. 131. 分割回文串

代码如下:

class Solution {
public:
    vector<vector<string>> ans;
    vector<string> path;
    vector<vector<string>> partition(string s) {
        backtracing(s, 0);
        return ans;
    }

    void backtracing(string& s, int startIndex) {
        if (startIndex >= s.length()) {
            ans.push_back(path);
        }

        string str;
        for (int i = startIndex; i < s.length(); ++i) {
            str = s.substr(startIndex, i - startIndex + 1);
            if (isPalindromic(str)) {
                path.push_back(str);
                backtracing(s, i + 1);
                path.pop_back();
            }
        }
    }

    bool isPalindromic(string& s) {
        for (int i = 0, j = s.length() - 1; i < j; i++, j--) {
            if (s[i] != s[j]) return false;
        }
        return true;
    }
};

// 针对回文子串优化
// 使用动态规划先计算出各个子串是否是回文串
class Solution {
private:
    vector<vector<string>> result;
    vector<string> path; // 放已经回文的子串
    vector<vector<bool>> isPalindrome; // 放事先计算好的是否回文子串的结果
    void backtracking (const string& s, int startIndex) {
        // 如果起始位置已经大于s的大小,说明已经找到了一组分割方案了
        if (startIndex >= s.size()) {
            result.push_back(path);
            return;
        }
        for (int i = startIndex; i < s.size(); i++) {
            if (isPalindrome[startIndex][i]) {   // 是回文子串
                // 获取[startIndex,i]在s中的子串
                string str = s.substr(startIndex, i - startIndex + 1);
                path.push_back(str);
            } else {                                // 不是回文,跳过
                continue;
            }
            backtracking(s, i + 1); // 寻找i+1为起始位置的子串
            path.pop_back(); // 回溯过程,弹出本次已经添加的子串
        }
    }
    void computePalindrome(const string& s) {
        // isPalindrome[i][j] 代表 s[i:j](双边包括)是否是回文字串 
        isPalindrome.resize(s.size(), vector<bool>(s.size(), false)); // 根据字符串s, 刷新布尔矩阵的大小
        for (int i = s.size() - 1; i >= 0; i--) { 
            // 需要倒序计算, 保证在i行时, i+1行已经计算好了
            for (int j = i; j < s.size(); j++) {
                if (j == i) {isPalindrome[i][j] = true;}
                else if (j - i == 1) {isPalindrome[i][j] = (s[i] == s[j]);}
                else {isPalindrome[i][j] = (s[i] == s[j] && isPalindrome[i+1][j-1]);}
            }
        }
    }
public:
    vector<vector<string>> partition(string s) {
        result.clear();
        path.clear();
        computePalindrome(s);
        backtracking(s, 0);
        return result;
    }
};