代码随想录算法训练营第29天-39. 组合总和-40.组合总和II-131.分割回文串

39. 组合总和

1.回溯

vector<vector<int>> res;
    vector<int> path;
    int sum;

    void backtracing(vector<int>& candidates, int target, int start)
    {
        if (sum > target) return;

        if (sum == target)
        {
            res.push_back(path);
            return;
        }

        for (int i = start; i < candidates.size(); i++)
        {
            path.push_back(candidates[i]);
            sum += candidates[i];
            backtracing(candidates, target, i);
            sum -= candidates[i];
            path.pop_back();
        }

    }

    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        res.clear();
        path.clear();

        if (candidates.empty()) return res;

        backtracing(candidates, target, 0);
        return res;
    }

2.剪枝

如果是升序可以剪枝,但这个题目candidates并没有说升序,没法剪掉

40.组合总和 II

这个题目主要问题在去重,如果想用set去重的话是做不到的,会超出内存限制

如果有相同数字的话,前面的情况一定会包含后面的,我们需要把后面的情况剪掉

首先对样例排序

这里我们以题目中的样例:1,1,2,5,6,7,10为例子

在下面的图中,可以看到第一个1的后续分支一定会包括了第二个1的后续分支,并且会额外包含用了两个1的情况(注意2nd:2,5,6,7,103rd:2,5,6,7,10含义是不同的,后者是用了两个1的;这里说的包含指的是2nd:1,2,5,6,7,10包含了2nd:2,5,6,7,10,两者都只取了一个1)

1st:1,1,2,5,6,7,10
2nd:1,2,5,6,7,10
2nd:2,5,6,7,10
后略
3rd:2,5,6,7,10
此后略

那么根据此,我们只需要把2nd:2,5,6,7,10这个分支剪掉就可以了。

这个剪掉的判别的条件:1.它不能是第一个;2.它和前面的一样(从start算起,可不是从0算起,从0算起上面的3rd:2,5,6,7,10可就被剪掉了,显然有错误)

vector<vector<int>> res;
vector<int> path;
int sum;

void backtracing(vector<int>& candidates, int target, int start)
{
    if (sum > target) return;

    if (sum == target)
    {
        res.push_back(path);
        return;
    }

    for (int i = start; i < candidates.size(); i++)
    {
        if (i > start && candidates[i] == candidates[i - 1]) continue;

        path.push_back(candidates[i]);
        sum += candidates[i];
        backtracing(candidates, target, i + 1);
        sum -= candidates[i];
        path.pop_back();
    }
}

vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
    res.clear();
    path.clear();

    if (candidates.empty()) return res;

    sort(candidates.begin(), candidates.end());
    backtracing(candidates, target, 0);

    return res;
}

2.代码随想录的方法

随想录的补充方法就是我想的这个,他的第一个用used标记的方法,这个方法used就是告诉你这个数字是否已经在path中

如果它用过了,那么你再往里放一样的元素就是合法的,可能的结果例如1,1,6

如果它没用过,那么你放一样的元素就认为是不合法的,就会产生1(first),2,51(second),2,5这种重复情况。

used可以优化掉,原理就是前面的方法,只需要发现它和之前的可选元素重复就认为不合法。再次强调一下,可选指的是从start开始,不是从0开始。

131.分割回文串

首先,返回全部切割结果就已经是一个问题了。

1.自己写的

我自己的思考路线是这样:

  • 1.需要一个判别回文的函数isReverse()

  • 2.需要一个能返回所有切片的回溯。

前者很容易实现,翻转前后是否一致即可判别。

后者相对复杂,因为后者在回溯的树上不只需要叶子结点,每一个结点都可能是结果,我们以abcd为例

可以看到,当最后一段只有一个字符时,即到达叶子结点,我们可以在回溯前加上字符串时利用isReverse()阻止掉不合法的切片方式。

但是,别忘了,这个树不是只有叶结点算切片方式,而是所有结点,因此我们在每次开始本轮切片时,需要先把当前的切片方式依照其是否合法纳入结果

abcd
a,bcd
a,b,cd
a,b,c,d
ab,cd
ab,c,d
abc,d
a,bc,d
vector<vector<string>> res;
vector<string> path;

bool isReverse(string str)
{
    string tmp = str;
    reverse(tmp.begin(), tmp.end());
    return str == tmp;
}

void backtracing(string str)
{
    //这里每次都针对上一层的最后一段,如果这一段是回文,那么这种切片合法(前面几段的合法性在上一层判别过,不合法不会进入这一层)
    if (isReverse(str))
    {
        path.push_back(str);
        res.push_back(path);
        path.pop_back();
    }


    for (int i = 0; i < str.size() - 1; i++)
    {
        string str1 = str.substr(0, i + 1);
        if (!isReverse(str1)) continue;

        path.push_back(str1);
        backtracing(str.substr(i + 1, str.size()));
        path.pop_back();
    }
}

vector<vector<string>> partition(string s) {
    res.clear();
    path.clear();

    if (s.empty()) return res;
    backtracing(s);
    return res;
}

2.代码随想录的方法

代码随想录的方法是基于原来的模板的思考方式。

我认为还是有必要照这个思路做一遍,因为感觉自己是对回溯不熟,所以写的代码并不是很模板化。

他的逻辑是一定要切到结尾才认为结束,这个思路我认为我应当学习,因为我没想到这一点,而是选择把所有节点归入结果判别,不是很符合回溯的逻辑

这个可能看文字不好理解,我们可以通过类似上面的图来理解。给每一个结点都多加了一个切到尾部的结点,从而使得问题回到那个只需要叶结点的回溯问题

abcd
a/bcd
a/b/cd
a/b/c/d
a/b/c/d/
ab/cd
ab/c/d
ab/c/d/
abc/d
abc/d/
abcd/
a/bc/d
a/bc/d/
a/bcd/
ab/cd/
void backtracing(const string& str, int startIndex)
{
    if (startIndex >= str.size())
    {
        res.push_back(path);
        return;
    }

    for (int i = startIndex; i < str.size(); i++)
    {
        string tmp = str.substr(startIndex, i - startIndex + 1);
        if (isReverse(tmp))
        {
            path.push_back(tmp);
            backtracing(str, i + 1);
            path.pop_back();
        }
        else
        {
            continue;
        }
    }
}

优化是提前算好是否回文,与回溯关系不大,就不写啦

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

去人777

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值