代码随想录算法训练营第23天|leetcode39.组合总和、leetcode40.组合2️⃣、leetcode131.分割回文串

1.39. 组合总和 - 力扣(LeetCode)

思路:这道题与前两个的不同点:组合没有数量要求;元素可无限重复选取。

但是有个点:最开始我看没有重复元素,所以我没有设置start那一项,发现就没法去重了。然后我发现2,2,3和3,2,2是一个,所以我发现得到的结果要单调递增,这样才能去重。

    vector<vector<int>> res;
    vector<int> path;
    void backtrack(vector<int> &candidates, int cnt,int start)
    {
        if (cnt == 0)
        {
            res.push_back(path);
            return;
        }
        if (cnt < 0)
            return;
        for (int i = start; i < candidates.size(); i++)
        {
            path.push_back(candidates[i]);
            backtrack(candidates, cnt - candidates[i], i);
            path.pop_back();
        }
        return;
    }
    vector<vector<int>> combinationSum(vector<int> &candidates, int target)
    {
        backtrack(candidates, target,0);
        return res;
    }

减枝后:

 for (int i = start; i < candidates.size() && cnt - candidates[i] >= 0; i++)

⚠️:这个for循环后半部分的剪枝,要求初始数组是递增的。

    vector<vector<int>> res;
    vector<int> path;
    void backtrack(vector<int> &candidates, int cnt, int start)
    {
        if (cnt == 0)
        {
            res.push_back(path);
            return;
        }
        if (cnt < 0)
            return;
        for (int i = start; i < candidates.size() && cnt - candidates[i] >= 0; i++)
        {
            path.push_back(candidates[i]);
            backtrack(candidates, cnt - candidates[i], i);
            path.pop_back();
        }
        return;
    }
    vector<vector<int>> combinationSum(vector<int> &candidates, int target)
    {
        sort(candidates.begin(), candidates.end());
        backtrack(candidates, target, 0);
        return res;
    }

2.40. 组合总和 II - 力扣(LeetCode)

思路:但是我去重打逻辑和代码随想录里的不太一样,我加了下面这行代码

  while (i < candidates.size() - 1 && candidates[i + 1] == candidates[i])
            {
                i++;
            }

我放在了没层for循环的的结尾,如果该层的candidates[i]与下层对应的candidates[i+1]相等,那就i++;因为如下图所示,排序后的数组连续的两个1连一起会造成数组的重复。

    vector<vector<int>> res;
    vector<int> path;
    void dfs(vector<int> &candidates, int cnt, int start)
    {
        if (cnt == 0)
        {
            res.push_back(path);
            return;
        }
        if (cnt < 0 || start > candidates.size() - 1)
            return;
        for (int i = start; i < candidates.size() && cnt - candidates[i] >= 0; i++)
        {
            path.push_back(candidates[i]);
            dfs(candidates, cnt - candidates[i], i + 1); // 每个数字只能使用一次
            path.pop_back();
            while (i < candidates.size() - 1 && candidates[i + 1] == candidates[i])
            {
                i++;
            }
        }
    }
    vector<vector<int>> combinationSum2(vector<int> &candidates, int target)
    {
        sort(candidates.begin(), candidates.end()); // 先排序,很重要,否则会出现1,7和7,1的问题
        dfs(candidates, target, 0);
        return res;
    }

3.131. 分割回文串 - 力扣(LeetCode)

思路:其实这道题就是遍历切割位置。如果startindex>=size()说明已经切割完毕了,即遍历完了。然后运用for循环去遍历从切割线到字符串末尾的位置,然后一直截取子串判断是不是回文数。如果是就存入path中,如果不是就continue(跳过)。

    vector<vector<string>> result;
    vector<string> path; // 放已经回文的子串
    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;
    }
    void backtracking(const string &s, int startIndex)
    { // 如果起始位置已经大于s的大小,说明已经找到了一组分割方案了
      // startIndex就是切割线
        if (startIndex >= s.size())
        {
            result.push_back(path);
            return;
        }
        for (int i = startIndex; i < s.size(); i++)
        { //[startIndex,i]是要截取的子串
            if (isPalindrome(s, 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();        // 回溯过程,弹出本次已经添加的子串
        }
    }
    vector<vector<string>> partition(string s)
    {
        if (s.size() == 0)
            return {};
        if (s.size() == 1)
            return {{s}};
        backtracking(s, 0);
        return result;
    }

但是上述代码是可以优化的,如下所示。上述代码isPalindrome函数运用双指针的方法来判定对于一个字符串s, 给定起始下标和终止下标, 截取出的子字符串是否是回文字串。但是其中有一定的重复计算存在:例如给定字符串"abcde", 在已知"bcd"不是回文字串时, 不再需要去双指针操作"abcde"而可以直接判定它一定不是回文字串。具体来说, 给定一个字符串s, 长度为n, 它成为回文字串的充分必要条件是s[0] == s[n-1]s[1:n-1]是回文字串

    vector<vector<string>> result;
    vector<string> path;               // 放已经回文的子串
    vector<vector<bool>> isPalindrome; // 放事先计算好的是否回文子串的结果
    void backtracking(const string &s, int startIndex)
    { // 如果起始位置已经大于s的大小,说明已经找到了一组分割方案了
      // startIndex就是切割线
        if (startIndex >= s.size())
        {
            result.push_back(path);
            return;
        }
        for (int i = startIndex; i < s.size(); i++)
        { //[startIndex,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 // 如果是不相邻字符,充要条件是s[0] == s[n-1]且s[1:n-1]是回文字串。
                {
                    isPalindrome[i][j] = (s[i] == s[j] && isPalindrome[i + 1][j - 1]);
                }
            }
        }
    }
    vector<vector<string>> partition(string s)
    {
        if (s.size() == 0)
            return {};
        if (s.size() == 1)
            return {{s}};
        computePalindrome(s);
        backtracking(s, 0);
        return result;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值