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

news2024/12/27 9:15:59

目录

  • 一、LeetCode 39. 组合总和
    • 思路:
    • C++代码
  • 二、LeetCode 40.组合总和II
    • 思路
    • C++代码
  • 三、LeetCode 131.分割回文串
    • 思路
    • C++代码
  • 总结


一、LeetCode 39. 组合总和

题目链接:LeetCode 39. 组合总和

文章讲解:代码随想录
视频讲解:带你学透回溯算法-组合总和(对应「leetcode」力扣题目:39.组合总和)| 回溯法精讲!

思路:

 题目要求从给出的数组里选取几个数,使得总和等于给出的target,并且数组中的数可以无限制重复选取,那么相对于前面做过的216. 组合总和 III来说,只需要在每层循环的时候考虑到上次选过的数字可以重复选取即可,即让循环条件中的初始值int i = pre:

for(int i = pre; i < candidates.size(); i++) //pre为上一层选取的数字的下标

 其余的代码基本不变,要找题目要求编程即可。

C++代码

class Solution {
private:
    vector<vector<int>> comb;
    vector<int> set;

    void backtrack(vector<int> candidates, int target, int sum, int pre){
        //pre记录前一个数字的下标
        if(sum > target) return; //剪枝操作
        if(sum == target){
            if(comb.size() < 150){
                comb.push_back(set);
            }
            return;
        }

        for(int i = pre; i < candidates.size(); i++){ 
            //同一个数字可以重复选取,所以可以从下标pre开始取
            set.push_back(candidates[i]);
            sum += candidates[i];
            backtrack(candidates, target, sum, i);
            sum -= candidates[i];
            set.pop_back();
        }
    }
public:
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        backtrack(candidates, target, 0, 0);
        return comb;
    }
};

二、LeetCode 40.组合总和II

题目链接:LeetCode 40.组合总和II

文章讲解:代码随想录
视频讲解:回溯算法中的去重,树层去重树枝去重,你弄清楚了没?| LeetCode:40.组合总和II

思路

 本题的重点在于:每个数字只能选一次,且给出的数组中会出现重复的数字,因此对于解集中的数组需要进行去重操作

 笔者在本题中采用的是在子集生成过程中,对子集树同一层的数进行的去重操作。
在这里插入图片描述
 如图,在子集树的遍历过程中,当数字选取在同一条树支上时,即选取数字都会出现在子集中时,重复数字是可以选取的;而当选取数字在同一树层上时,即在同一个位置上选取数字时,不能重复选取。

 体现在代码中则是当前数字与前一个数字相同,且前一个数字没有被选取时,那么当前的数字就不能选取(因为如果选取,那么后续生成的所有子集都会和选取前一个数生成的子集相同,出现重复),所以我们在递归中加上一个判断条件即可去重。

C++代码

class Solution {
private:
    vector<vector<int>> comb;
    vector<int> set;
    vector<bool> used;
    int sum;

    void backtrack(vector<int> candidates, int target, int pre){
        if(sum == target){
            comb.push_back(set);
            return;
        }
        for(int i = pre + 1; i < candidates.size() && sum + candidates[i] <= target; i++){
            if(i > 0 && candidates[i] == candidates[i-1] && !used[i-1]) continue;
            //遇到同一层相同数字,则跳过此次循环
            set.push_back(candidates[i]);
            sum += candidates[i];
            used[i] = true;
            backtrack(candidates, target, i);
            used[i] = false;
            sum -= candidates[i];
            set.pop_back();
        }
    }
public:
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        sum = 0;
        sort(candidates.begin(), candidates.end());
        for(auto x: candidates){
            used.push_back(false);
        }
        backtrack(candidates, target, -1);
        return comb;
    }
};

三、LeetCode 131.分割回文串

题目链接:LeetCode 131.分割回文串

文章讲解:代码随想录
视频讲解:带你学透回溯算法-分割回文串(对应力扣题目:131.分割回文串)| 回溯法精讲!

思路

 题目要求将给出的字符串划分为几个回文串的组合,笔者采用回溯算法。对于回文串的判断和处理,笔者采用延伸生成的方法,如图:
在这里插入图片描述
 对于一个已经确定的回文串,如果他的左右两边的字符相等的话,那么加上左右两边的字符会变成一个更长的字符串(这一点适用于奇数长度与偶数长度);因此笔者的算法思想为:在当前未确定的字符串部分找到一个最短的回文串,在这个最短回文串的基础上向两边延伸生成新的回文串;于是对于回溯算法的设计即是在每一层对于最短的回文串进行操作。
在这里插入图片描述
 如图,对于每层回溯的递归逻辑,将当前遍历到的字符作为回文串生成的中心位置字符,在此基础上进行延伸;具体延伸方法为:在原回文串基础上,判断原回文串两侧的字符是否相等,如果相等就可以加入到原串两侧,完成延伸,进入下一层for循环继续延伸;否则跳出for循环。

 回溯递归函数的设计依旧是分为三部曲来编写:

 递归函数的传参以及返回值,笔者设计回溯算法无返回值,设置全局变量储存回文串的分割方案;传参方面,由于笔者在递归函数中处理的是回文串最中间的字符,因此需要一个下标记录当前的字符位置,同时需要另一个下标记录回文串可以到达的最左端的位置(用来控制for循环),因此递归的传参和返回为:

void backtrack(int cur, int pre)

 终止条件设计为,递归函数传入的当前字符的位置大于原串的长度,将当前方案存入全局变量中,递归函数返回。

if (cur >= size) {
	palindrome.push_back(set);
	return;
}

 递归函数逻辑如下:

 由于该算法中,奇数长度与偶数长度的回文串生成方式有区别,所以需要分开判断延伸的条件:

//延伸条件判断
if (palindrome[0][cur - i] == palindrome[0][cur + i]) //奇数长度回文串

if (palindrome[0][cur - i] == palindrome[0][cur + i + 1]) //偶数长度回文串

 由于奇数长度可以直接将当前的字符作为第一个回文串,偶数长度需要进行一次判断才能确定第一个回文串,因此在循环逻辑和循环条件上也有区别,因此奇数长度和偶数长度笔者分成两个for循环来写。

for (int i = 0; i < size - cur && i <= cur - pre; i++) //奇数长度回文串

for (int i = 0; i < size - cur - 1 && i <= cur - pre; i++) //偶数长度回文串

for循环中,当满足两端字符相等的延伸条件时,扩展回文串,并且删除前面一个字符(因为前面一个字符被包含进当前的回文串了),进入下一层递归。

 由于本题和笔者算法的特殊性,在递归返回时不直接进行回溯,而是当回文串两端字符不相等时,即不能再延伸出更长的回文串时,将回文串中包含过的左侧的元素依次返还,便于返回上一层递归继续操作:

int b = odd.size() / 2; //遍历完毕,归还前面的元素
for (int j = 0; j < b; j++) {
  string tmp(1, odd[j]);
  set.push_back(tmp);
}

C++代码

class Solution {
private:
    vector<vector<string>> palindrome;
    vector<string> set;
    int size;
    void backtrack(int cur, int pre) {
        if (cur >= size) {
            palindrome.push_back(set);
            return;
        }

        string odd = "";
        string even = "";

        for (int i = 0; i < size - cur && i <= cur - pre; i++) {//奇数长度回文串
            if (odd.empty()) {  //奇数个存在一个的情况,除此以外的情况与偶数个类似
                odd = palindrome[0][cur];
                set.push_back(odd);
                backtrack(cur + 1, pre);
                set.pop_back();
            }
            else {
                if (palindrome[0][cur - i] == palindrome[0][cur + i]) {
                    odd = palindrome[0][cur - i] + odd + palindrome[0][cur - i];
                    set.pop_back();
                    set.push_back(odd);
                    backtrack(cur + i + 1, cur + i + 1);
                    set.pop_back();
                }
                else {
                    break;
                }
            }
        }
        int b = odd.size() / 2; //遍历完毕,归还前面的元素
        for (int j = 0; j < b; j++) {
            string tmp(1, odd[j]);
            set.push_back(tmp);
        }

        for (int i = 0; i < size - cur - 1 && i <= cur - pre; i++) {
            if (palindrome[0][cur - i] == palindrome[0][cur + i + 1]) { //偶数长度回文串
                even = palindrome[0][cur - i] + even + palindrome[0][cur - i];
                if (i > 0) set.pop_back();
                set.push_back(even);
                backtrack(cur + i + 2, cur + i + 2);
                set.pop_back();
            }
            else {
                break;
            }
        }
        b = (even.size() / 2) - 1; //遍历完毕,归还元素
        for (int j = 0; j < b; j++) {
            string tmp(1, even[j]);
            set.push_back(tmp);
        }
    }
public:
    vector<vector<string>> partition(string s) {
        size = s.size();
        for (auto x : s) { //构建一个初始集合,便于生成
            string pal;
            pal.push_back(x);
            set.push_back(pal);
        }
        palindrome.push_back(set);
        set.clear();
        backtrack(0, 0);
        palindrome.erase(palindrome.begin()); 
        //构建的初始集合和生成的第一个集合重复,因此需要删除一个(可优化)
        return palindrome;
    }
};


总结

 回溯法的进阶应用。在编写回溯算法时仍要注意递归的逻辑结构、终止条件和剪枝条件、循环的边界条件,以及递归返回时数值的恢复(回溯),都是回溯算法的重点问题。


文章图片来源:代码随想录 (https://programmercarl.com/)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2082301.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

直播平台直播API集成之快手篇

前言: 本篇我们来介绍如何使用快手 的直播API创建直播。 准备工作: 1、你首先得有个快手账号; 2、创建快手应用,填写应用审核信息,等待应用创建审核通过,应用成功创建后在开发与上线前还要提前做好API权限申请,如果你只需要获取用户基本信息,以及得到直播API的访问权限…

Python | Leetcode Python题解之第377题组合总和IV

题目&#xff1a; 题解&#xff1a; class Solution:def combinationSum4(self, nums: List[int], target: int) -> int:dp [1] [0] * targetfor i in range(1, target 1):for num in nums:if num < i:dp[i] dp[i - num]return dp[target]

合宙LuatOS产品规格书——Air700EMQ

Air700EMQ是合宙通信的LTE Cat.1bis通信模块&#xff0c; 依托移芯EC716E平台&#xff0c;支持先进的LTE 3GPP Rel.13技术。 主要特点如下&#xff1a; 1. 技术平台与标准支持&#xff1a; Air700EMQ采用移芯EC716E平台&#xff0c;基于先进的LTE技术。支持LTE 3GPP Releas…

leetcode234. 回文链表(java实现)

题目描述&#xff1a; 本道题的思路可以使用集合先存储链表的值&#xff0c;然后进行判断即可。 总体思路比较简单。 代码实现&#xff1a; class Solution {public boolean isPalindrome(ListNode head) {List<Integer> res new ArrayList();ListNode cur head;whil…

3分钟快速本地搭建RSShub服务器并结合内网穿透实现无公网IP远程访问

文章目录 前言1. Docker 安装2. Docker 部署Rsshub3. 本地访问Rsshub4. Linux安装Cpolar5. 配置公网地址6. 远程访问Rsshub7. 固定Cpolar公网地址8. 固定地址访问 前言 今天和大家分享的是如何在本地快速简单部署Rsshub工具&#xff0c;并结合cpolar内网穿透工具使用公网地址远…

Remote Sensing(MDPI)期刊投稿历程(CV方向)

一、期刊简介 期刊官网&#xff1a;https://www.mdpi.com/journal/remotesensing 影响因子&#xff08;2024&#xff09;&#xff1a;4.2 分区&#xff1a;JCR:Q1。中科院二区 版面费&#xff1a;2700瑞士法郎&#xff08;约21000rmb&#xff09; 二、投稿时间线 2024.06.20…

科研绘图系列:R语言对角线矩阵图(corrplot plot)

介绍 对角线矩阵图(Diagonal Matrix Plot)是一种特殊类型的图表,用于可视化对角线矩阵中的元素。对角线矩阵是一种方阵,其中非对角线上的元素都是零,而对角线上的元素可以是任意值。这种矩阵在数学和计算机科学中非常有用,尤其是在线性代数、特征值问题和对角化等操作中…

C语言中的野指针

野指针是指指针指向的位置是随机的&#xff0c;不明确的&#xff0c;未知的&#xff0c;没有限制的。 野指针的成因 指针未初始化 如上图&#xff0c;指针没有初始化&#xff0c;则指针指向的地址是随机的&#xff0c;若该地址已经被其他程序占用&#xff0c;且对指针进行解引…

C++入门基础(内容太干,噎住了)

文章目录 1.缺省参数 2.函数重载 2.1重载条件&#xff1a; 1.参数类型不同 2.参数个数不同 3.参数类型顺序不同 2.2不做重载条件情况&#xff1a; 1.返回值相同时 2.当全缺省遇见无参数 3.引用 3.1引用特性&#xff1a; 3.2引用的使用 1.缺省参数 1.缺省参数是声明…

中庸就是五五开,各打五十大板吗

中庸指的是&#xff0c;人生行事应该把握一个度&#xff0c;既不过分&#xff0c;也无不及&#xff0c;以中为贵&#xff0c;以和为美。 中庸关键在于“中”&#xff0c;要求适中适度、恰如其分。 《论语•先进》中提到&#xff0c;有一次&#xff0c;子路问孔子&#xff0c;…

Springboot中使用Elasticsearch(部署+使用+讲解 最完整)

Elasticsearch&#xff1a;用于数据存储、计算和搜索 Mysql&#xff1a;擅长事务类型操作&#xff0c;可以确保数据的安全和一致性 Elasticsearch&#xff1a;擅长海量数据的搜索、分析、计算 基于这个特点我打算改造用户方面的功能&#xff0c;基于用户量比较多&#xff0c;…

电影《名侦探柯南:百万美元的五棱星》 观后感

上周看了电影《名侦探柯南&#xff1a;百万美元的五棱星》,个人觉得这可能是相对于柯南系列的重度粉丝来说是相当不错的&#xff0c;对于自己这个外行的人来说&#xff0c;可能有些故事背景并不了解&#xff0c;以及里边出场的人物并不熟悉&#xff0c;整体看下来觉得算是中规中…

复习Vue笔记

笔记来源于黑马程序员相关上课笔记 基于脚手架创建前端工程 环境要求 node.js&#xff1a;前端项目的运行环境&#xff08;相当于Java的运行环境JDK&#xff09; npm&#xff1a;JS的包管理工具/器 npm腾讯镜像&#xff1a;npm config set registry http://mirrors.cloud.te…

BaseCTF-Misc-Week2-WP

目录 1、二维码1-街头小广告 2、反方向的雪 3、黑丝上的flag 4、海上又遇了鲨鱼 5、Base?! 6、ez_crypto 7、前辈什么的最喜欢了 8、哇&#xff01;珍德食泥鸭 9、Aura 酱的旅行日记 I 10、Aura 酱的旅行日记 II 11、Aura 酱的旅行日记 III 12、Aura 酱的旅行日…

Docker镜像制作(使用GPU)

由于最近参加天池的大模型比赛&#xff0c;在复赛阶段需要制作并提交Docker进行模型的推理&#xff0c;因此在这里记录一下Docker制作的过程。 准备 featurize阿里云账号 由于需要使用GPU资源&#xff0c;因此选择了预装Docker的featurize服务器。 开通阿里云容器镜像服务 …

74HC595的用法与原理

DS/LDSI&#xff08;SER&#xff09;&#xff0c;串行数据输入引脚 OE/LEDN&#xff0c;输出使能控制脚&#xff0c;它是低电才使能输出&#xff0c;所以接GND RCK/LDSTR&#xff08;STCP&#xff09;&#xff0c;存储寄存器时钟输入引脚。上升沿时&#xff0c;数据从移位寄存器…

万万没想到!秋季里的这些水果,对帕金森治疗大有裨益!——蔡英丽医生

随着秋风轻拂&#xff0c;大地披上了一袭金黄色的外衣&#xff0c;这不仅是收获的季节&#xff0c;更是自然对人类健康的又一次慷慨馈赠。对于帕金森病患者而言&#xff0c;这个秋季尤为特别&#xff0c;因为大自然悄然间准备了五种神奇的秋季水果&#xff0c;它们不仅是味蕾的…

命令模式详解

命令模式 简介:命令模式将一个请求封装为一个对象&#xff0c;从而使你可以用不同的请求对客户进行参数化&#xff0c;对请求排队或记录请求日志&#xff0c;以及支持可撤销的操作。 人话: 总体来说, 就是一个命令类, 一个执行类, 命令类包括执行类, 然后在外部添加一个总的管…

SQLi-LABS通关攻略【41-45】

SQLi-LABS 41关 这一关是堆叠注入 测试闭合 ?id1 //回显错误 ?id1-- //回显错误 ?id1-- //回显正确 所以是数字型的注入 测试堆叠注入&#xff0c;更改Dumb的密码 ?id1;update users set password123456 where usernameDumb-- SQLi-…

2008-2024年名爵汽车维修手册和电路图线路图接线图资料更新

经过整理&#xff0c;2009-2024年名爵汽车全系列已经更新至汽修帮手资料库内&#xff0c;覆盖市面上99%车型&#xff0c;包括维修手册、电路图、新车特征、车身钣金维修数据、全车拆装、扭力、发动机大修、发动机正时、保养、电路图、针脚定义、模块传感器、保险丝盒图解对照表…