代码随想录 -- day27 -- 39. 组合总和、40.组合总和II、131.分割回文串

图解移动机器人中的运动规划(Motion Planning),附几十种规划算法 运动规划是移动机器人的核心理论和工程技术之一,本文作为教程先导,解析了运动规划的两大组件——路径规划和轨迹规划。欢迎感兴趣的同学订阅专栏! 阅读详情

39. 组合总和

 回溯三部曲:

  • 递归函数参数

这里依然是定义两个全局变量,二维数组result存放结果集,数组path存放符合条件的结果。(这两个变量可以作为函数参数传入)

首先是题目中给出的参数,集合candidates, 和目标值target。

此外我还定义了int型的sum变量来统计单一结果path里的总和,其实这个sum也可以不用,用target做相应的减法就可以了,最后如何target==0就说明找到符合的结果了,但为了代码逻辑清晰,我依然用了sum。

本题还需要startIndex来控制for循环的起始位置。

vector<vector<int>> result;
vector<int> path;
void backtracking(vector<int>& candidates, int target, int sum, int startIndex)
  • 递归终止条件

如果sum > target 直接返回,如果等于就把path加入到result里。

if (sum > target) {
    return;
}
if (sum == target) {
    result.push_back(path);
    return;
}
  • 单层搜索的逻辑

依旧是从i = startIndex开始遍历,但是本题是可以无限重复的,所以递归的时候还是从i开始。

for (int i = startIndex; i < candidates.size(); i++) {
    sum += candidates[i];
    path.push_back(candidates[i]);
    backtracking(candidates, target, sum, i); // 关键点:不用i+1了,表示可以重复读取当前的数
    sum -= candidates[i];   // 回溯
    path.pop_back();        // 回溯
}
class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& candidates, int target, int sum, int startIndex) {
        if (sum > target) {
            return;
        }
        if (sum == target) {
            result.push_back(path);
            return;
        }

        for (int i = startIndex; i < candidates.size(); i++) {
            sum += candidates[i];
            path.push_back(candidates[i]);
            backtracking(candidates, target, sum, i); // 不用i+1了,表示可以重复读取当前的数
            sum -= candidates[i];
            path.pop_back();
        }
    }
public:
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        result.clear();
        path.clear();
        backtracking(candidates, target, 0, 0);
        return result;
    }
};

40.组合总和II

区别:

  1. 本题candidates 中的每个数字在每个组合中只能使用一次。
  2. 本题数组candidates的元素是有重复的,而39.组合总和 (opens new window)是无重复元素的数组candidates

 本题的难点在于区别2中:集合(数组candidates)有重复元素,但还不能有重复的组合

重复元素有两个类型:树枝上重复和树层上重复。我们要去重的是同一树层上的“使用过”,同一树枝上的都是一个组合里的元素,不用去重。

 回溯三部曲:

  • 确定递归函数和参数

used数组是用来去重的

vector<vector<int>> result; // 存放组合集合
vector<int> path;           // 符合条件的组合
void backtracking(vector<int>& candidates, int target, int sum, int startIndex, vector<bool>& used) {
  • 确定终止条件
if (sum > target) { // 这个条件其实可以省略
    return;
}
if (sum == target) {
    result.push_back(path);
    return;
}
  • 单层遍历

如果candidates[i] == candidates[i - 1] 并且 used[i - 1] == false,就说明:前一个树枝,使用了candidates[i - 1],也就是说同一树层使用过candidates[i - 1]。

此时for循环里就应该做continue的操作。

for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++) {
    // used[i - 1] == true,说明同一树枝candidates[i - 1]使用过
    // used[i - 1] == false,说明同一树层candidates[i - 1]使用过
    // 要对同一树层使用过的元素进行跳过
    if (i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == false) {
        continue;
    }
    sum += candidates[i];
    path.push_back(candidates[i]);
    used[i] = true;
    backtracking(candidates, target, sum, i + 1, used); // 和39.组合总和的区别1:这里是i+1,每个数字在每个组合中只能使用一次
    used[i] = false;
    sum -= candidates[i];
    path.pop_back();
}
class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& candidates, int target, int sum, int startIndex, vector<bool>& used) {
        if (sum == target) {
            result.push_back(path);
            return;
        }
        for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++) {
            // used[i - 1] == true,说明同一树枝candidates[i - 1]使用过
            // used[i - 1] == false,说明同一树层candidates[i - 1]使用过
            // 要对同一树层使用过的元素进行跳过
            if (i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == false) {
                continue;
            }
            sum += candidates[i];
            path.push_back(candidates[i]);
            used[i] = true;
            backtracking(candidates, target, sum, i + 1, used); // 和39.组合总和的区别1,这里是i+1,每个数字在每个组合中只能使用一次
            used[i] = false;
            sum -= candidates[i];
            path.pop_back();
        }
    }

public:
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        vector<bool> used(candidates.size(), false);
        path.clear();
        result.clear();
        // 首先把给candidates排序,让其相同的元素都挨在一起。
        sort(candidates.begin(), candidates.end());
        backtracking(candidates, target, 0, 0, used);
        return result;
    }
};


131.分割回文串

回文子串是正着读和反着读都一样的字符串。

本题这涉及到两个关键问题:

  1. 切割问题,有不同的切割方式
  2. 判断回文

回溯三部曲:

  • 确定递归函数和参数
vector<vector<string>> result;
vector<string> path; // 放已经回文的子串
void backtracking (const string& s, int startIndex) {
  • 终止条件
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(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();        // 回溯过程,弹出本次已经添加的子串
}

注意切割过的位置,不能重复切割,所以,backtracking(s, i + 1); 传入下一层的起始位置为i + 1。

#判断回文子串

最后我们看一下回文子串要如何判断了,判断一个字符串是否是回文。

可以使用双指针法,一个指针从前向后,一个指针从后向前,如果前后指针所指向的元素是相等的,就是回文字符串了

 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;
 }
class Solution {
private:
    vector<vector<string>> result;
    vector<string> path; // 放已经回文的子串
    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(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(); // 回溯过程,弹出本次已经添加的子串
        }
    }
    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();
        backtracking(s, 0);
        return result;
    }
};

18、地下矿山中人工智能在区分真实微震事件与噪声信号的应用 本文提出一种基于遗传编程(GP)的新型经验模型,用于地下矿山中真实微震事件与噪声信号(特别是爆破信号)的自动区分。针对传统方法和常见机器学习算法存在的黑箱问题、过拟合及准确性不足等局限,GP模型通过符号回归和进化机制生成可解释的数学表达式,具备自动特征选择能力,有效提升了分类准确率。研究使用包含100个样本的真实数据库,结合敏感性分析优化模型参数,并与线性判别分析(LDA)进行对比,结果表明GP模型在分类性能上更具优势。该模型可应用于实时微震监测系统,提高矿山安全生产水平,降低误判带来的成本开销,具有良好的 阅读详情

相关推荐

Windows防火墙服务异常修复指南:0x80070422与0x6D9报错实战处理

本文针对Windows防火墙服务异常导致的0x80070422与0x6D9报错,提供了从基础到进阶的实战修复指南。核心是通过services.msc服务管理器检查并修复Windows Firewall服务,包括设置启动类型、检查依赖服务等关键步骤。同时介绍了当基础方法失效时,如何使用命令行工具、系统文件检查器(SFC/DISM)以及排查组策略与第三方软件冲突等深度解决方案,帮助用户彻底解决防火墙服务无法启动或访问的问题。

m9n0o的博客 542

代码随想录Day27 | 39. 组合总和 40.组合总和II 131.分割回文串

Day27

sRiddle的博客 1183

避坑指南:Qwen2.5-VL图片预处理中常见的5个尺寸错误

本文深入解析了Qwen2.5-VL模型在图片预处理环节中开发者最常遇到的五个尺寸错误,包括动态分辨率对齐、宽高比超标、像素数限制等问题。文章结合源码解读,详细剖析了`process_vision_info`函数和`resize`逻辑,并提供了具体的排查流程图与参数设置建议,旨在帮助开发者构建健壮的预处理流水线,有效规避显存溢出与推理失败的风险。

weixin_29290379的博客 176

零基础代码随想录【Day27】|| 39. 组合总和,40.组合总和II, 131.分割回文串

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。注意图中叶子节点的返回条件,因为本题没有组合数量要求,仅仅是总和的限制,所以递归没有层数的限制,只要选取的元素总和超过target,就返回!注意题目中给我们 集合是有重复元素的,那么求出来的 组合有可能重复,但题目要求不能有重复组合。的区别是:本题没有数量要求,可以无限重复,但是有总和的限制,所以间接的也是有个数的限制。解集不能包含重复的组合。

Andrew______的博客 1238

基于AR和SLAM技术的商场智能导视系统技术原理详解

本文针对智慧商场室内导航需求,提出基于AR和SLAM技术的商场智能导视系统技术方案,实现了沉浸式导览体验并促进消费转化,助力商场运营升级。

维小帮定位导航的博客 1243

投第一篇SCIE英文期刊(详细步骤)

一个投稿SCIE英文期刊的过程

weixin_48849679的博客 1万+

代码随想录Day27|39.组合总和、40.组合总和II、131.分割回文串

代码随想录Day27|39.组合总和、40.组合总和II、131.分割回文串

weixin_46620779的博客 1900

「点燃我,温暖你」用Python制作一个动态爱心效果

最近「点燃我,温暖你」这部剧非常火,讲述的是程序员的爱情故事。其中陈飞宇饰演的男主李峋,在剧中用程序做出的爱心跳动效果,非常炫。

Modeler_xiaoyu的博客 1万+

代码随想录算法训练营三刷day27 | 回溯之 39. 组合总和 40.组合总和II 131.分割回文串

此外我还定义了int型的sum变量来统计单一结果path里的总和,其实这个sum也可以不用,用target做相应的减法就可以了,最后如何target==0就说明找到符合的结果了,但为了代码逻辑清晰,我依然用了sum。以及上面的版本一的代码大家可以看到,对于sum已经大于target的情况,其实是依然进入了下一层递归,只是下一层递归结束判断的时候,会判断。(这两个参数可以放到函数参数里)可以使用双指针法,一个指针从前向后,一个指针从后向前,如果前后指针所指向的元素是相等的,就是回文字符串了。

baimaozi_007的博客 967

OData 请求里的 Nextlink 实战:在 ABAP Client Proxy 中优雅处理长列表分页

本文介绍了OData v4标准中的@odata.nextLink分页机制及其在SAP ABAP中的实践应用。重点解析了该机制通过服务端提供的完整URL实现分页获取,客户端只需循环处理直到该字段消失的特点。文章提供了清晰的ABAP代码模板,展示如何通过Remote Client Proxy实现分页数据获取,并强调了仅支持OData v4和远程消费的限制条件。通过HR员工数据聚合的实际案例,说明了流式分页处理的最佳实践,包括服务端过滤、稳定排序和内存控制等关键技术要点,为ABAP开发者提供了可直接落地的解决方案

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 269

PCL 直通滤波器【2026最新版】

PCL直通滤波器函数详解以及用于多字段滤波、强度滤波、获取某一时间范围内的点等高阶用法案例分享。博客长期更新,本文最近一次更新时间为:2026年3月11日。

点云侠的博客 40万+

代码随想录day27|39. 组合总和|40.组合总和II|131.分割回文串|Golang

笔记

weixin_42161901的博客 447

使用podspec创建iOS插件

概述 在WWDC 2014全球开发者大会上,苹果开放了动态库、App Extension等全新的功能,这为iOS插件化开发带来了可能。在iOS开发中,动态库是iOS提供的一种资源打包方式,可以将代码文件、头文件、资源文件和说明文档等集中在一起,并且可以在运行时进行动态加载。 目前,很多应用越做越复杂,应用程序也显得越来越臃肿。试想一下,如果将一些不常用的功能做成一个插件,然后根据用户的需求从网络中...

xiangzhihong8的专栏 991

代码随想录算法训练营day27 | 39. 组合总和、40.组合总和II、131.分割回文串

LeetCode | 39. 组合总和、40.组合总和II、131.分割回文串

weixin_69675605的博客 839

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

代码随想录算法训练营Day27 | 39. 组合总和、40.组合总和II、131.分割回文串 | Python | 个人记录向

回溯算法:39. 组合总和、40.组合总和II、131.分割回文串。

Python基本算法 1093

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

代码随想录刷题day27 39. 组合总和;40.组合总和II;131.分割回文串

代码随想录刷题day27

weixin_43127049的博客 334

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

代码随想录算法训练营Day 27| 回溯算法part03 | 39. 组合总和、40.组合总和II、131.分割回文串

如果candidates[i] == candidates[i - 1] 并且 used[i - 1] == false,就说明:前一个树枝,使用了candidates[i - 1],也就是说同一树层使用过candidates[i - 1]。本题数组candidates的元素是有重复的,而39.组合总和 (opens new window)是无重复元素的数组candidates。2.树层去重,要去重的是“同一树层上的使用过”,如何判断同一树层上元素(相同的元素)是否使用过了呢。

m0_51759998的博客 1077

代码随想录 Day27 39. 组合总和 40.组合总和II 131.分割回文串

2. 本题的特殊点就是本题是可以允许重复的元素存在,但是不允许数组中出现重复元素的情况,也就是说同一种情况【1,2,3】不允许改换顺序以后再写成【3,2,1】或者其他以后在算作一种情况。所以说本题就需要额外设置一个flag使得每次进行下一次递归的时候下一次的递归是从该点开始的,这个地方无关元素值的问题。1. 在for循环中for(int i = startIndex;1. 本题相较于前面的题目的问题就是去重,但是所给的原来的元素集合中还是有相同的元素的,所以就比较复杂,需要设置used数组进行标记。

weixin_61057535的博客 728

代码随想录算法训练营 DAY 27 | 39.组合总和 40.组合总和 II 131.分割回文串

虽然都是一进for循环里就判断,但一个是continue一个是break!continue代表跳到下一个i+1开始搜,不进入下一层递归。break代表直接结束整个for循环,终止搜索!初始化used数组可以用切割回文串问题要联想到组合问题,startIndex相当于切割线,终止条件就是起始位置≥s的长度。在单层逻辑里判断是不是回文串,是就直接substring收集子串,不是就continue(直接跳到下一个i,不会进入下一层递归)。这样可以直接在终止条件里收集path了。substring是左闭右开!!

qq_35904005的博客 1145

代码随想录二刷day27 | 回溯之 39. 组合总和 40.组合总和II 131.分割回文串

回溯之 39. 组合总和 40.组合总和II 131.分割回文串

baimaozi_007的博客 191

代码随想录算法训练营day27 || 39. 组合总和,40.组合总和II,131.分割回文串

带着这个思想我们进行递归函数的编写,这个思想直接指明的是回溯的循环部分怎么写,遍历的内容应该是子串的长度,如此我们就可以对每个子串进行控制,并且可以精确的确定每个划分的位置;既然循环遍历的是每个子串的长度,那么遍历结束的标志,即遍历完整个字符串就是截止条件,否则递归无法终结,也相应的推导出了所需要的截止条件,完成递归回溯的整体函数。思路:不同于以往的组合类型的题目,该题所给出的数据中出现了重复元素,因此在存在着重复元素的集合中输出所有的满足要求的组合,需要对组合进行去重。思路:题目中给出这样一句话:“

旭 571

代码随想录算法训练营Day27|39. 组合总和、40.组合总和II、131.分割回文串

本题的组合求和对数组中的数字可以无限制重复选取,本题没有组合数量要求,仅仅是总和的限制,所以递归内有层数的限制,只要选取的元素总和超过target就返回。本题与上一题的不同之处在于本题candidates数组中的每个数字在每个组合中只能使用一次。本题的难点在于集合有重复元素,但不能有重复的组合,在具体实现上要理清树层去重和树枝去重。切割问题类似组合问题,也可以抽象为树形结构。本题递归参数还需要startIndex,因为切割过的地方不能重复切割,和组合问题也是一致的。

m0_68682181的博客 669

代码随想录刷题笔记 DAY 27 | 组合总和 No.39 | 组合求和 II No.40 | 分割回文串 No.131

输入: candidates = [10,1,2,7,6,1,5], target = 8,输入: candidates = [2,5,2,1,2], target = 5,其实参考上图很容易看出来,对于一个节点的遍历从数组中这节点开始的,所以对于。输入:candidates = [2,3,6,7], target = 7。输出: [[2,2,2,2],[2,3,3],[3,5]]输出:[[“a”,“a”,“b”],[“aa”,“b”]]的字符串的所有的切割情况,其实和组合问题是十分类似的。

kkq的博客 1260

代码随想录算法训练营DAY27|C++回溯算法Part.3|39.组合总和、40.组合总和II、组合问题小总结、131.分割回文串

什么时候要用startIndex来控制for循环,什么时候不用呢?如果是一个集合来求组合的话,就需要用startIndex,例如:77.组合,216.组合总和III如果是多个集合取组合,各个集合之间相互不影响,那么就用startIndex,例如:17.电话号码的字母组合。

糖炒栗子 962

代码随想录刷题day27 | 39. 组合总和、40.组合总和II、131.分割回文串

代码随想录刷题day27

weixin_40323978的博客 423

代码随想录训练营day27|39、组合总和;40、组合总和II;131、分割回文串

39、组合总和;40、组合总和II;131、分割回文串

weixin_43561267的博客 81
上一篇: 代码随想录 -- day25 -- 216.组合总和III、17.电话号码的字母组合
下一篇: 代码随想录 -- day28 -- 93.复原IP地址 、78.子集 、90.子集II
七人酒
博客等级 码龄4年 51粉丝 65原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值