回溯問題

胖柚の工作室發表於2024-07-25

17.電話號碼的字母組合

class Solution {
public:
    string mp[10] = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
    vector<string> ans;

    vector<string> letterCombinations(string digits) {
        int n = digits.size();
        if (n == 0) return {};
        string p(n, 0);
        function<void(int)> dfs = [&](int u) {//當前列舉到第u位
            if (u == n) {//列舉到最後一位的後一位,說明選擇字元結束,將
                ans.push_back(p);
                return;
            }
            for (auto c : mp[digits[u] - '0']) {
                p[u] = c;//直接覆蓋
                dfs(u + 1);//列舉下一位
            }
        };
        dfs(0);//從第0位開始列舉
        return ans;
    }
};

78.子集

思考角度一、考慮每個數選或不選,這樣列舉到 \(i\) = \(n\) 時說明所有情況考慮完畢,退出 \(\rm dfs\)

class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        int n = nums.size();
        vector<vector<int>> ans;
        if (n == 0) return {};
        vector<int> p;
        function<void(int)> dfs = [&](int i) {//列舉第i個數選或不選
            if (i == n) {
                ans.push_back(p);
                return;
            }
            dfs(i + 1);//不選這個數,直接跳過,列舉下一個數
            p.push_back(nums[i]);//選這個數
            dfs(i + 1);
            p.pop_back();//恢復現場
        };
        dfs(0);
        return ans;
    }
};

思考角度二、
答案的角度考慮:列舉第 \(i\) 個位置應該選原陣列中的哪個數。(\(i\) 表示列舉的起點)

image

class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        int n = nums.size();
        vector<vector<int>> ans;
        vector<int> p;
        function<void(int)> dfs = [&](int i) {
            ans.push_back(p);
            if (i == n) return;//由於i = n時不進入迴圈,這行可以省略
            for (int j = i; j < n; j++) {
                p.push_back(nums[j]);
                dfs(j + 1);
                p.pop_back();
            }         
        };
        dfs(0);
        return ans;
    }
};

131.分割回文串

思考角度一、假設每對相鄰字元之間有個逗號,那麼就看每個逗號是選還是不選。

class Solution {
public:
    bool is(string a, int l, int r) {
        while (l < r) {
            if (a[l++] != a[r--]) return false;
        }
        return true;
    }

    vector<vector<string>> partition(string s) {
        vector<vector<string>> ans;
        vector<string> p;
        int n = s.size();
        function<void(int, int)> dfs = [&](int i, int u) {//i表示起始位置,u表示當前列舉到的位置
            if (u == n) {
                ans.push_back(p);
                return;
            }
            if (u != n - 1) dfs(i, u + 1);
            if (is(s, i, u)) {
                p.push_back(s.substr(i, u - i + 1));
                dfs(u + 1, u + 1);
                p.pop_back();
            }
        };
        dfs(0, 0);
        return ans;
    }
};

思考角度二、依次列舉每個字串結束(逗號)的具體位置

class Solution {
public:
    bool is(string a, int l, int r) {
        while (l < r) {
            if (a[l++] != a[r--]) return false;
        }
        return true;
    }

    vector<vector<string>> partition(string s) {
        int n = s.size();
        vector<vector<string>> ans;
        vector<string> p;
        function<void(int)> dfs = [&](int i) {
            if (i == n) {
                ans.push_back(p);
                return;
            }
            for (int j = i; j < n; j++) {
                if (is(s, i, j)) {
                    p.push_back(s.substr(i, j - i + 1));
                    dfs(j + 1);
                    p.pop_back();
                }
            }
        };
        dfs(0);
        return ans;
    }
};

相關文章