LeetCode 93 复原IP地址
题目链接:https://leetcode.cn/problems/restore-ip-addresses/
思路:
思路和切割回文子串类似,不同的在于是通过逗点(.)去判断终止条件,合法IP只会有三个逗点,所以定义一个变量pointsum记录逗点的数量,当pointsum=3的时候就达到了终止条件,此时要判断第四段子串是否合法,合法则写入结果集中。
代码:
class Solution {
private:
vector<string>result;
// [start,end]
bool isValid(const string& s,int start,int end)
{
if(start>end)
return false;
if(s[start]=='0'&&start!=end) // 说明此时有零做开头,且不是个位数,即012的情况。
return false;
int num = 0;
// 遇到<0或者>9的数不合法
for(int i = start;i<=end;i++)
{
if(s[i]<'0'||s[i]>'9')
return false;
// 判断是否大于255
num = num*10+(s[i]-'0');
if(num>255)
return false;
}
return true;
}
void backtracking(string&s,int startindex,int pointsum)
{
if(pointsum==3) // 说明此时已经有了三个逗点(.),此时需要判断第四段子串是否合法,合法则写入结果集
{
if(isValid(s,startindex,s.size()-1))
{
result.push_back(s);
return;
}
}
for(int i = startindex;i<s.size();i++) // 横向切割
{
// 子串[startindex,i]
if(isValid(s,startindex,i))
{
// 添加逗点(.)
s.insert(s.begin()+i+1,'.');
pointsum++;
// 向下递归
backtracking(s,i+2,pointsum); // 此时startindex=i+2是因为添加了一个逗点(.)
// 回溯
s.erase(s.begin()+i+1);
pointsum--;
}
else // 不合法说明再横向切割也是不合法的
break;
}
}
public:
vector<string> restoreIpAddresses(string s) {
result.clear();
if(s.size()<4) return result;
backtracking(s,0,0);
return result;
}
};
总结
学习如何判断终止条件
LeetCode 78 子集
题目链接:https://leetcode.cn/problems/subsets/
思路:
和组合还有分割问题不同的是,子集问题是在树的每一个节点都要收集结果;而组合和分割问题是在叶子节点收集结果。
代码:
class Solution {
private:
vector<vector<int>>result;
vector<int>path;
void backtracking(vector<int>& nums,int startindex)
{
// 这样就可以每一个节点都收集结果
result.push_back(path);
if(startindex>=nums.size())
return;
for(int i = startindex;i<nums.size();i++)
{
path.push_back(nums[i]);
backtracking(nums,i+1);
path.pop_back();
}
}
public:
vector<vector<int>> subsets(vector<int>& nums) {
result.clear();
backtracking(nums,0);
return result;
}
};
总结
学到了如何在每个节点处都收集结果
LeetCode 90 子集II
题目链接:https://leetcode.cn/problems/subsets-ii/
思路:
该题是78.子集和40.组合总和II的结合,使用used数组去判断是否有重复子集。进行树层去重。
代码:
class Solution {
private:
vector<vector<int>>result;
vector<int>path;
void backtracking(vector<int>&nums,int startindex,vector<bool>&used)
{
result.push_back(path);
if(startindex>=nums.size())
return;
for(int i = startindex;i<nums.size();i++)
{
if(i>0&&nums[i]==nums[i-1]&&used[i-1]==0)
continue;
used[i] = true;
path.push_back(nums[i]);
backtracking(nums,i+1,used);
path.pop_back();
used[i] = false;
}
}
public:
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
vector<bool>used(nums.size(),false);
result.clear();
path.clear();
sort(nums.begin(),nums.end());
backtracking(nums,0,used);
return result;
}
};
总结
相对简单。
今日总结:
复习了分割问题,学习了子集问题。总的都是套用回溯模板,关键在于终止条件和如何加入结果集。