【蓝桥杯速成】| 10.回溯切割

前面两篇内容我们都是在做有关回溯问题的组合应用

今天的题目主题是:回溯法在切割问题的应用

题目一:分割回文串

问题描述

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

给你一个字符串 s,请你将 s 分割成一些 子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。

示例 1:

输入:s = "aab"
输出:[["a","a","b"],["aa","b"]]

示例 2:

输入:s = "a"
输出:[["a"]]

提示:

  • 1 <= s.length <= 16
  • s 仅由小写英文字母组成

解题步骤

在做这题之前我们需要明确回文串是什么东西👇

那么我们解决这个问题就可以分为分割字符串和判断回文串两个part

运用回溯法分割字符串其实和组合问题是类似的思路

我们需要按照字符串顺序,在所有可能位置砍上一刀,形成不同的字符串碎片

那么为了不错过任何一种可能,就需要按顺序遍历

下面就来尝试使用回溯三部曲!

1.确定函数返回值及参数

依旧使用backtracking这个名字,没有返回值的要求,参数呢传入我们需要使用的字符串s以及一个开始索引startindex即可

这个开始索引是不断变大的,意味着每次砍第一刀下去,第一个碎片的长度在慢慢变大,这也是我们有序分割的重要环节

void backtracking(string s,int startindex)

2.确定终止条件

按照最简单的思路,和之前的组合问题一样,我们会在叶子结点取到该过程的一个答案,再做个对应题目限制的判断,合适就加入result中, 

那么我们的分割也是这样,在叶子节点结束分割,把整个字符串剁碎了,

但需要注意的是,在叶子节点我们得到的不是碎片,而是虽然碎了但仍旧可以拼成完整s的组合体

就像你买了一只烤鸭,咔咔剁了几刀,你拿到手的是所有鸭子碎片而不是只有一个鸭屁股

那么在这种情况下,加入判断回文子串的代码好像有点不方便

所以我们改为在单层递归的时候做判断而不是在终止条件做判断

这样做同时也更省事了,如果你切了一刀发现这个不是回文串就没必要再切下去了

就好比切到鸭屁股的话扔掉就好了,没必要把鸭屁股也剁碎留着

还有一点需要明确的是分割完毕的表示方法,

在参数列表中我们不断传递的startindex实际上就对应我们砍下去的那一刀,

那么只要这个索引大于等于回文串长度就是切完了

if(startindex>=s.size()){

        result.push_back(path);

        return;

}

 3.确定单层遍历操作

还是同样的逻辑,最外层确定第一刀位置

切下来以后加入path中,再递归切第二刀...

递归结束后需要pop加入值,做一个回溯

除了以上切割操作,我们还需要加入判断回文串的代码

这一个部分为使效率最大化可以加在把碎片加入path的代码前

也就是说符合回文才加入path,否则直接return

判断回文串我们可以另设函数所以单层遍历操作应该如下:

for(int i=startindex;i<s.size();i++){

        if(isPalindrome(...){//这个函数等会再想!

                string str = s.substr(startindex,i-startindex+1);//当前碎片是【startindex,i】

                path.push_back(str);//是回文串!切下来放进path中!

        }else{

                continue;//不是就跳出!

        }

        backtracking(s,i+1);

        path.pop_back();

}       

补充:

s.substr(pos, len)

substr()是C++语言函数,主要功能是复制子字符串,要求从指定位置开始,并具有指定的长度。如果没有指定长度_Count或_Count+_Off超出了源字符串的长度,则子字符串将延续到源字符串的结尾。 

 ok那么回溯算法的步骤我们已经完成了,还剩下判断回文串的逻辑需要写

4.判断回文串

按照回文串的概念,我们只需要一个指针从头开始,一个指针从尾开始

不断同频比较两个字符是否相等就可以了

那么需要的参数就是这个字符串碎片

返回值设为bool类型

bool isPalindrome(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;

}

合并所有代码,并在主函数中传参调用即可!完整代码看下面👇! 

code

class Solution {
public:vector<string> path;vector<vector<string>> result;bool isPalindrome(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(string s,int startindex){if(startindex>=s.size()){result.push_back(path);return;}for(int i=startindex;i<s.size();i++){if(isPalindrome(s,startindex,i)){string str=s.substr(startindex,i-startindex+1);path.push_back(str);}else{continue;}backtracking(s,i+1);path.pop_back();}}vector<vector<string>> partition(string s) {backtracking(s,0);return result;}
};

题目二:复原IP地址

问题描述

93. 复原 IP 地址 - 力扣(LeetCode)

有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。

  • 例如:"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址,但是 "0.011.255.245""192.168.1.312" 和 "192.168@1.1" 是 无效 IP 地址。

给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 '.' 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。

示例 1:

输入:s = "25525511135"
输出:["255.255.11.135","255.255.111.35"]

示例 2:

输入:s = "0000"
输出:["0.0.0.0"]

示例 3:

输入:s = "101023"
输出:["1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"]

提示:

  • 1 <= s.length <= 20
  • s 仅由数字组成

解题步骤

参照上一题,我一开始写了以下代码,

主要思路就是无情分割,直到最后,

在每一步分割中使用函数判断是否符合要求

合理就加入path,逐渐组成IP地址

最后在叶子节点处加入正确的IP到result中

但是发现在单层递归中很难处理,

所以我们需要改变一下整个回溯的过程,按照这个题目的特点,给出量身定制的方案

class Solution {

public:

    string path;

    vector<string> result;

    bool isOK(string str){

        if(str.empty() || str.size()>3 || str[0]=='0'){

            return false;

        }

        int num=stoi(str);

        return num>=0&&num<=255;

    }

    void backtracking(string s,int startindex){

        if(startindex>=s.size()){

            result.push_back(path);

            return;

        }

        for(int i=startindex;i<s.size();i++){

            string str=s.substr(startindex,i-startindex+1);

            if(isOK(str)){

                path+=str;

               //@#@¥¥#%#!@@¥#@%!@?????

            }else{

                break;

            }

            backtracking(s,i+1);

            path.pop_back();

        }

    }

    vector<string> restoreIpAddresses(string s) {

        if(s.size()<4 || s.size()>12){

            return result;

        }

        backtracking(s,0);

        return result;

    }

};

 再审审题!

IP地址是只能有4段的,每一段不超过3个数字,

所以这里我们需要把段数作为切割完成的标志,不能再用索引指到最后,

这样就相当于做数学证明题有条件没用上,那一般来说都是证不出来的!!!

那么和段数有关的还有IP地址中的 '.' (点),

为了一举两得(实现判断段数和加点)我们可以用点数来代表段数,

点数为3意味着段数为4嘛!

重新走一边回溯三部曲

1.确定函数返回值及参数

按照上面的思路,我们得加入段数作为参数,搭配startindex共同表示分割情况

startindex关乎下刀点,不能丢掉,把刀丢了还怎么切嘛!

void backtracking(string s,int startindex,int pointnum)

2.确定终止条件

上面已经捋清楚了:点数为3意味着段数为4

但是加入第三个点后我们还没有判断剩下部分,也就是第四段是否符合要求

所以这个最后一部分的合法性要放在这个终止条件中进行判断

合法后再加入到result中

if(pointnum==3){

        if(isValid...){//判断合法性等会再说!

                result.push_back(s);

        }

        return;      

}

 3.确定单层遍历操作

在单层遍历中我们需要往s中加点(题目说了可以通过在 s 中插入 '.' 来形成),修改pointnum,递归,回溯

最外层遍历依旧代表第一刀(或者说上一刀)的位置,

这样才能确保第二🔪是从剩下的部分切的

切下来一段就需要判断是否合法

如果合法,在s的这个位置后面加上点

再让pointnum+1

递归backtracking函数

pointnum回溯,

s回溯

如果不合法,那么直接结束

for(int i=startindex;i<s.size();i++){

        if(isValid...){

                s.insert(s.begin()+i+1,'.');//加点

                pointnum++;

                backtracking(s,i+2,pointnum);//加入点后要再后挪一位!

                pointnum--;

                s.erase(s.begin()+i+1);

        }

        else

                break;

}

4.编写判断合法性函数

作为一个判断类的函数,返回值肯定是bool类型,

参数是为了传入待判断的片段,所以依旧是s,start,end

在函数体中利用if 判断IP地址一段的数字区间是否在[0,255],每一段开头第一个是否为0,区间长度是否小于等于3,该片段是否为空

否就返回false,都没有踩雷就返回true

bool isValid(string& s,int start,int end){   

        if (start > end || end - start + 1 > 3) {

            return false; // 片段为空或长度超过3

        }

        if (s[start] == '0' && start != end) {

            return false; // 前导0不合法

        }

        string str = s.substr(start, end - start + 1);

        int num = stoi(str);

        return num >= 0 && num <= 255;

}        

 最后整合代码,传入对应参数,在主函数中调用即可,

此外,在主函数中还可以加入一个剪枝,

如果字符串长度明显不符合IP地址的长度就不用调用回溯算法了

if(s.size()<=0 || s.size()>12)

        return result;

完整代码在下方! 

code 

class Solution {
public:vector<string> result;bool isValid(string& s,int start,int end){if (start > end || end - start + 1 > 3) {return false; // 片段为空或长度超过3}if (s[start] == '0' && start != end) {return false; // 前导0不合法}string str = s.substr(start, end - start + 1);int num = stoi(str);return num >= 0 && num <= 255;}     void backtracking(string s,int startindex,int pointnum){if(pointnum==3){if(isValid(s,startindex,s.size()-1)){//最后一段!result.push_back(s);}return;}for(int i=startindex;i<s.size();i++){if(isValid(s,startindex,i)){s.insert(s.begin()+i+1,'.');//加点pointnum++;backtracking(s,i+2,pointnum);pointnum--;s.erase(s.begin()+i+1);}elsebreak;}}vector<string> restoreIpAddresses(string s) {if(s.size()<=0 || s.size()>12)return result;backtracking(s,0,0);return result;}
};

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

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

相关文章

【嵌入式硬件】三款DCDC调试笔记

关于开关电源芯片&#xff0c;重点关注输入电源范围、输出电流、最低压降。 1.MP9943: 以MP9943为例&#xff0c;输入电压范围4-36V&#xff0c;输出最大电流3A&#xff0c;最低压降为0.3V 调整FB使正常输出为5.06V 给定6V空载、5V空载、5V带2A负载的情况&#xff1a; 6V带2A…

2025年03月18日柯莱特(外包宁德)一面前端面试

目录 自我介绍你怎么从0到1搭建项目的webpack 的构建流程手写webpack插件你有什么想问我的吗 2. 你怎么从 0 到 1 搭建项目的 在面试中回答从 0 到 1 搭建前端项目&#xff0c;可按以下详细步骤阐述&#xff1a; 1. 项目前期准备 需求理解与分析 和产品经理、客户等相关人…

在vitepress中使用vue组建,然后引入到markdown

在 VitePress 中&#xff0c;每个 Markdown 文件都被编译成 HTML&#xff0c;而且将其作为 Vue 单文件组件处理。这意味着可以在 Markdown 中使用任何 Vue 功能&#xff0c;包括动态模板、使用 Vue 组件或通过添加 <script> 标签为页面的 Vue 组件添加逻辑。 值得注意的…

Jupyter Notebook 常用命令(自用)

最近有点忘记了一些常见命令&#xff0c;这里就记录一下&#xff0c;懒得找了。 文章目录 一、文件操作命令1. %cd 工作目录2. %pwd 显示路径3. !ls 列出文件4. !cp 复制文件5. !mv 移动或重命名6. !rm 删除 二、代码调试1. %time 时间2. %timeit 平均时长3. %debug 调试4. %ru…

Java面试黄金宝典12

1. 什么是 Java 类加载机制 定义 Java 类加载机制是 Java 程序运行时的关键环节&#xff0c;其作用是把类的字节码文件&#xff08;.class 文件&#xff09;加载到 Java 虚拟机&#xff08;JVM&#xff09;中&#xff0c;并且将字节码文件转化为 JVM 能够识别的类对象。整个类…

第十四章:模板实例化_《C++ Templates》notes

模板实例化 核心知识点解析多选题设计题关键点总结 核心知识点解析 两阶段查找&#xff08;Two-Phase Lookup&#xff09; 原理&#xff1a; 模板在编译时分两个阶段处理&#xff1a; 第一阶段&#xff08;定义时&#xff09;&#xff1a;检查模板语法和非依赖名称&#xff0…

LSM-Tree(Log-Structured Merge-Tree)详解

1. 什么是 LSM-Tree? LSM-Tree(Log-Structured Merge-Tree)是一种 针对写优化的存储结构,广泛用于 NoSQL 数据库(如 LevelDB、RocksDB、HBase、Cassandra)等系统。 它的核心思想是: 写入时只追加写(Append-Only),将数据先写入内存缓冲区(MemTable)。内存数据满后…

LangChain组件Tools/Toolkits详解(6)——特殊类型注解Annotations

LangChain组件Tools/Toolkits详解(6)——特殊类型注解Annotations 本篇摘要14. LangChain组件Tools/Toolkits详解14.6 特殊类型注解Annotations14.6.1 特殊类型注解分类14.6.1 InjectedToolArg构建运行时绑定值工具14.6.3 查看并传入参数14.6.4 在运行时注入参数14.6.5 其它特…

openharmony中hilog实证记录说明(3.1和5.0版本)

每次用这个工具hilog都有一些小用法记不清&#xff0c;需要花一些时间去查去分析使用方法&#xff0c;为了给丰富多彩的生活留出更多的时间&#xff0c;所以汇总整理共享来了&#xff0c;它来了它来了~~~~~~~~~ 开始是想通过3.1来汇总的&#xff0c;但实际测试发现openharmony…

NVIDIA nvmath-python:高性能数学库的Python接口

NVIDIA nvmath-python&#xff1a;高性能数学库的Python接口 NVIDIA nvmath-python是一个高性能数学库的Python绑定&#xff0c;它为Python开发者提供了访问NVIDIA优化数学算法的能力。这个库特别适合需要高性能计算的科学计算、机器学习和数据分析应用。 文章目录 NVIDIA nv…

【euclid】20 2D包围盒模块(box2d.rs)

box2d.rs文件定义了一个二维轴对齐矩形&#xff08;Box2D&#xff09;&#xff0c;使用最小和最大坐标来表示。矩形在坐标类型&#xff08;T&#xff09;和单位&#xff08;U&#xff09;上是泛型的。代码提供了多种方法来操作和查询矩形&#xff0c;包括求交集、并集、平移、缩…

ChatTTS 开源文本转语音模型本地部署 API 使用和搭建 WebUI 界面

ChatTTS&#xff08;Chat Text To Speech&#xff09;&#xff0c;专为对话场景设计的文本生成语音(TTS)模型&#xff0c;适用于大型语言模型(LLM)助手的对话任务&#xff0c;以及诸如对话式音频和视频介绍等应用。支持中文和英文&#xff0c;还可以穿插笑声、说话间的停顿、以…

链表相关知识总结

1、数据结构 基本概念&#xff1a; 数据项&#xff1a;一个数据元素可以由若干个数据项组成数据对象&#xff1a;有相同性质的数据元素的集合&#xff0c;是数据的子集数据结构&#xff1a;是相互之间存在一种或多种特定关系的数据元素的集合 逻辑结构和物理结构&#xff1a…

蓝桥杯备考-》单词接龙

很明显&#xff0c;这道题是可以用DFS来做的&#xff0c;我们直接暴力搜索&#xff0c;但是这里有很多点是我们需要注意的。 1.我们如何确定两个单词能接上&#xff1f; 比如touch和choose 应该合成为touchoose 就是这样两个单词&#xff0c;我们让一个指针指着第一个字符串…

C语言-访问者模式详解与实践

C语言访问者模式详解与实践 - 传感器数据处理系统 1. 什么是访问者模式&#xff1f; 在嵌入式系统中&#xff0c;我们经常需要对不同传感器的数据进行多种处理&#xff0c;如数据校准、过滤、存储等。访问者模式允许我们在不修改传感器代码的情况下&#xff0c;添加新的数据处…

(UI自动化测试web端)第二篇:元素定位的方法_xpath路径定位

1、第一种xpath路径定位&#xff1a; 绝对路径&#xff1a;表达式是以 /html开头&#xff0c;元素的层级之间是以 / 分隔相同层级的元素可以使用下标&#xff0c;下标是从1开始的需要列出元素所经过的所有层级元素&#xff0c;工作当中一般不使用绝对路径 例&#xff1a;/html/…

设置GeoJSONVectorTileLayer中的line填充图片

设置GeoJSONVectorTileLayer中的line填充图片 关键&#xff1a;linePatternFile const style [{filter: true,renderPlugin: {dataConfig: {type: "line",},type: "line",},symbol: {linePatternFile: "http://examples.maptalks.com/resources/pat…

electron框架(4.0)electron-builde和electron Forge的打包方式

----使用electron-builder打包&#xff08;需要魔法&#xff09; --安装electron-builder: npm install electron-builder -D--package.json中进行相关配置&#xff1a; {"name": "video-tools","version": "1.0.0","main&quo…

让 MGR 不从 Primary 的节点克隆数据?

问题 MGR 中&#xff0c;新节点在加入时&#xff0c;为了与组内其它节点的数据保持一致&#xff0c;它会首先经历一个分布式恢复阶段。在这个阶段&#xff0c;新节点会随机选择组内一个节点&#xff08;Donor&#xff09;来同步差异数据。 在 MySQL 8.0.17 之前&#xff0c;同…

第三十二篇 深入解析Kimball维度建模:构建企业级数据仓库的完整框架

目录 一、维度建模设计原则深度剖析1.1 业务过程驱动设计1.2 星型模式VS雪花模式 二、维度建模五步法实战&#xff08;附完整案例&#xff09;2.1 业务需求映射2.2 模型详细设计2.3 缓慢变化维处理 三、高级建模技术解析3.1 渐变维度桥接表3.2 快照事实表设计 四、性能优化体系…