当前位置: 首页 > news >正文

【蓝桥杯速成】| 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);
            }
            else
                break;
        }
    }

    vector<string> restoreIpAddresses(string s) {
        if(s.size()<=0 || s.size()>12)
            return result;
        backtracking(s,0,0);
        return result;
    }
};

相关文章:

  • Spring Boot(十七):集成和使用Redis
  • 【正点原子】AI人工智能深度学习(RV1126/RK3568/RK3588)-第1期 准备篇
  • 【Android】VehiclePropertyAccess引起CarService崩溃
  • AI比人脑更强,因为被植入思维模型【21】冯诺依曼思维模型
  • HarmonyOS Next~鸿蒙图形开发技术解析:AREngine与ArkGraphics 2D的核心能力与应用实践
  • 雷电模拟器启动94%卡住不动解决方案
  • 谷歌Gemini代码助手免费版解析:技术革新与用户隐私的权衡
  • Tomcat中间件漏洞攻略
  • 2. AVL树
  • 查询、插入、更新、删除数据的SQL语句(SQLite)
  • Ceph集群部署步骤
  • Python---数据分析(Pandas十一:二维数组DataFrame统计计算二)
  • 《AI大模型趣味实战 》第8集:多端适配 个人新闻头条 基于大模型和RSS聚合打造个人新闻电台(Flask WEB版) 2
  • 开源AI大模型、AI智能名片与S2B2C商城小程序源码:实体店引流的破局之道
  • 指针,数组 易混题解析(一)
  • 【STM32】SPI通信外设硬件SPI读写W25Q64
  • 手动离线安装NextCloud插件
  • ElementUI表格展开属性
  • AI与自媒体的深度融合路径、场景与挑战
  • 从 Java 的 Spring Boot MVC 转向 Go 语言开发的差异变化
  • 美联储连续第三次维持利率不变,警示关税影响
  • 李干杰走访各民主党派中央和全国工商联机关
  • 中俄领导人将讨论从俄罗斯经蒙古至中国天然气管道项目?外交部回应
  • 央行:将支持资本市场两项货币政策工具的额度合并使用
  • 吴清:创造条件支持优质中概股企业回归内地和香港股市
  • 潘功胜:将下调个人住房公积金贷款利率0.25个百分点