回溯 切割问题

leetcode 131

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

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

示例 1:

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

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

思路

由于也是将字符串中的a拿出来,然后再与字符串当中的字母进行组合,所以其实也相当于组合问题,只不过,还多了一个判断是否为回文子串的问题。
所以可以考虑用回溯三部曲来分析做题。

返回值与参数

全局变量 result与path分别存储全部的结果与每次的结果,另外还可以添加一个参数startIndex,记录遍历的位置,也就是初始的位置,方便往下递归时候调用。

vector<vector<string>> result;//存储全部的结果
vector<string> path;
void backtracking(const string& s,int starIndex )

递归函数与终止条件

如果选取的位置大于或者等于s字符串的大小,那么就可以判断为取到合适的回文字符串;

if(startIndex >= s.size()){
result.push_back(path);
return;
}

遍历

for依然适用于横向遍历,递归适合往下遍历;这道题当中,还有一个问题就是判断所取得的字符串是否为回文。

for(int i = starIndex;i < s.size();i++)
        {
            if(isPalindrome(s,starIndex,i)){
                //判断是否是回文子串,获取[startIndex]在s中的子串
                string str = s.substr(starIndex,i - starIndex + 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;
    }

所有代码

class Solution {
private:
    vector<vector<string>> result;//存储全部的结果
    vector<string> path;
    void backtracking(const string& s,int starIndex ){
        if(starIndex >= s.size())
        { 
            //递归终止条件,是否大于该大小,如果大于就说明获得了一个数组
            result.push_back(path);
            return;
        }
        for(int i = starIndex;i < s.size();i++)
        {
            if(isPalindrome(s,starIndex,i)){
                //判断是否是回文子串
                string str = s.substr(starIndex,i - starIndex + 1);
                path.push_back(str);
            }
            else{
                continue;
            }
            backtracking(s,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;
    }
};

小结

本题难点:

  • 判断这个为回溯问题:将分割问题抽象为回溯
  • 判断的依据:题目字眼中有分割,重新组合,需要for循环多次遍历的
  • 本题当中,还需要考虑的一个问题就是判断字符串回文

leetcode 93

题目描述:
有效 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"]

思路

切割问题,可以直接套用回溯三部曲进行分析

参数与返回值

本题主要就是分割数字段,除了记录下一层递归分割的起始位置的参数startIndex,还需要一个记录分段点数值pointNum;

vector<string> result;
//startIndex记录起始值,pointNum记录点的数量
void backtracking(string& s,int startIndex,int pointNum)

终止条件与递归函数

因为要分4段,所有就需要切三次,所以点数值保证在3就可以终止了,其次就是保证该字段的有效性。

if(pointNum == 3)
        {
           if(isValid(s, startIndex, s.size()-1))//需要添加判断字段有效性的函数
           {
               result.push_back(s);
           }
           return;
        }

遍历

for 横向遍历,递归函数向下遍历;

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;
        }

判断网络段的有效性,有三点:

  • 段位不能以0开头
  • 段位里有非正整数字符不合法
  • 不能大于255

所以函数为:

bool isValid(const string& s,int start,int end)
    {
        if(start > end)
        {
            return false;
        }
        if(s[start]=='0' && start != end)
        {
            return false;
        }
        int num = 0;
        for(int i = start;i <= end;i++){
            if (s[i] > '9' || s[i] < '0')
            {
                return false;
            }
            num = num*10 + (s[i] - '0');
            if(num > 255)
            {
                return false;
            }
        }
        return true;
    }

全部代码

class Solution {
private:
    vector<string> result;
    //startIndex记录起始值,pointNum记录点的数量
    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;
        }
    }
    bool isValid(const string& s,int start,int end)
    {
        if(start > end)
        {
            return false;
        }
        if(s[start]=='0' && start != end)
        {
            return false;
        }
        int num = 0;
        for(int i = start;i <= end;i++){
            if (s[i] > '9' || s[i] < '0')
            {
                return false;
            }
            num = num*10 + (s[i] - '0');
            if(num > 255)
            {
                return false;
            }
        }
        return true;
    }
public:
    vector<string> restoreIpAddresses(string s) {
        result.clear();
        if (s.size() > 12) return result;
        backtracking(s,0,0);
        return result;

    }
};

本题小结

难点:

  • 切割问题转化为回溯算法思考
  • 对于题设当中的关键参数,能不能转化为代码
  • 本题的判断段位值的有效性
posted @ 2022-10-13 10:38  JaydenHuan  阅读(18)  评论(0编辑  收藏  举报