题目链接:93. 复原 IP 地址 - 力扣(LeetCode)
文章链接:代码随想录 (programmercarl.com)
视频链接:回溯算法如何分割字符串并判断是合法IP?| LeetCode:93.复原IP地址
这道题在理解“分割”后,就比较简单了,与分割回文串不同,这道题限制了分割的次数,只允许分割成四段,也就是3个'.',所以我们就可以通过一个int参数pointnum记录记录'.'的个数,终止条件就是pointnum==3.另外还需要判定最后一段是否是有效字符串,所以终止条件的代码就出来:
if(pointnum==3&&isvalid(s,index,s.size()-1)==true)
result.push_back(s);
除此之外,就是需要自定义一个函数,来确定是否是有效字符串,需要满足几个条件。
1.不可以有前导0
if(s[start]=='0'&&start!=end) return false;//不含前导0
2.每一段字符串都不能大于255
3.不可以出现0-9以外的字符
for(int i=start;i<=end;i++){
if(s[i]<'0'||s[i]>'9') return false;
num=num*10+(s[i]-'0');
if(num>255) return false;
}
4.每一段字符串都不能为空
if(start>end) return false;
自定义函数代码如下:
bool isvalid(string& s,int start,int end){
if(start>end) return false;//防止出现最后一段没有字符的情况
if(s[start]=='0'&&start!=end) return false;//不含前导0
int num=0;
for(int i=start;i<=end;i++){
if(s[i]<'0'||s[i]>'9') return false;
num=num*10+(s[i]-'0');
if(num>255) return false;
}
return true;
}
总代码如下:
class Solution {
public:
vector<string> result;
bool isvalid(string& s,int start,int end){
if(start>end) return false;//防止出现最后一段没有字符的情况
if(s[start]=='0'&&start!=end) return false;//不含前导0
int num=0;
for(int i=start;i<=end;i++){
if(s[i]<'0'||s[i]>'9') return false;
num=num*10+(s[i]-'0');
if(num>255) return false;
}
return true;
}
void backtracking(string& s,int index,int pointnum)
{
if(pointnum==3&&isvalid(s,index,s.size()-1)==true)
result.push_back(s);
for(int i=index;i<s.size();i++)
{
if(isvalid(s,index,i)==true)
{
s.insert(s.begin()+1+i,'.');
backtracking(s,i+2,pointnum+1);//因为加了个'.',所以需要i+2
s.erase(s.begin()+1+i);
}else break;
}
}
vector<string> restoreIpAddresses(string s) {
backtracking(s,0,0);
return result;
}
};
78.子集
文章链接:代码随想录 (programmercarl.com)
视频链接:回溯算法解决子集问题,树上节点都是目标集和! | LeetCode:78.子集
这道题在我们做了这么多回溯题值后,还是比较easy的。找到所有的子集,包括空集和本身,甚至连终止条件都不需要。
只需要在每一次回溯的过程时把元素存进path,再将path存进result就好。
class Solution {
public:
vector<int> path;
vector<vector<int>> result;
void backtracking(vector<int>nums,int index){
result.push_back(path);
for(int i=index;i<nums.size();i++){
path.push_back(nums[i]);
backtracking(nums,i+1);
path.pop_back();
}
return;
}
vector<vector<int>> subsets(vector<int>& nums) {
backtracking(nums,0);
return result;
}
};
90.子集II
文章链接:代码随想录 (programmercarl.com)
视频链接::回溯算法解决子集问题,如何去重?| LeetCode:90.子集II
这道题与上一题的区别在于这道题集合中可能包含重复元素,所以需要采用去重操作。
去重操作有三个关键点:1.对数组重新排序。 2.相同元素的只遍历一次,剩下的都跳过
3.使用bool数组used记录上一元素是否“使用过”
Day27有讲过去重的具体操作
for(int i=index;i<candidates.size()&& sum + candidates[i] <= target;i++){
if(i>0&&candidates[i]==candidates[i-1]&&used[i-1]==false){//去重
continue;
}
path.push_back(candidates[i]);
used[i]=true;
backtracking(candidates,target,sum+candidates[i],i+1,used);
used[i]=false;
path.pop_back();
}
总代码如下:
class Solution {
public:
vector<int> path;
vector<vector<int>> result;
void backtracking(vector<int>& nums,int index,vector<bool>&used)
{
result.push_back(path);
for(int i=index;i<nums.size();i++){
if(i>0&&nums[i]==nums[i-1]&&used[i-1]==false){//去重
continue;
}
path.push_back(nums[i]);
used[i]=true;
backtracking(nums,i+1,used);
used[i]=false;
path.pop_back();
}
return;
}
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
sort(nums.begin(),nums.end());//排序
vector<bool> used(nums.size(),false);
backtracking(nums,0,used);
return result;
}
};
Day28打卡成功,耗时3.5小时,对去重操作进行巩固,并对回溯算法有了更深刻的理解,再接再厉!