class Solution {
public:
vector<string> res;
vector<string> restoreIpAddresses(string s) {
if (s.size() < 4 || s.size() > 12) return res; // 算是剪枝了!!!
backtracking(s, 0, 0);
return res;
}
void backtracking(string& s, int start_index, int point_num){
if(start_index >= s.size()) return;
if(point_num == 3) {
//注意s.size() - start_index!!!
string s_sub = s.substr(start_index, s.size() - start_index);
if(isValid(s_sub)) res.push_back(s);
return;
}
for(int i = start_index; i < s.size(); i++){
string s_sub = s.substr(start_index, i - start_index + 1); //注意获取子串的方法!!!注意i - start_index + 1!!!
if(isValid(s_sub)) {
s.insert(s.begin() + i + 1, '.'); //注意s.insert的使用!!!!!!
backtracking(s, i + 2, point_num + 1); //注意这里是从i+ 2开始
s.erase(s.begin() + i + 1);
}
else continue;
}
}
//验证是否是有效的IP子串
bool isValid(string& s_sub){
if(s_sub.size() > 3 || (s_sub[0] == '0' && s_sub.size() > 1)) return false;
int temp = 0;
for(int i = 0; i < s_sub.size(); i++){
if(s_sub[i] - '0' < 0 || s_sub[i] - '0' > 9) return false;
temp = temp*10 + (s_sub[i] - '0');
}
if(temp > 255) return false;
else return true;
}
};
求子集问题和77.组合 (opens new window)和131.分割回文串 (opens new window)不一样。
如果把 子集问题、组合问题、分割问题都抽象为一棵树的话,那么组合问题和分割问题都是收集树的叶子节点,而子集问题是找树的所有节点!
其实子集也是一种组合问题,因为它的集合是无序的,子集{1,2} 和 子集{2,1}是一样的。
那么既然是无序,取过的元素不会重复取,写回溯算法的时候,for就要从startIndex开始,而不是从0开始!求取子集问题,不需要任何剪枝!因为子集就是要遍历整棵树。
class Solution {
public:
vector<int> path;
vector<vector<int>> res;
vector<vector<int>> subsets(vector<int>& nums) {
// 收集子集,要放在终止添加的上面,否则会漏掉自己
backtracking(nums, 0);
return res;
}
void backtracking(vector<int>& nums, int start_index){
res.push_back(path);
for(int i = start_index; i < nums.size(); i++){
path.push_back(nums[i]);
backtracking(nums, i + 1);
path.pop_back();
}
}
};
这道题目和78.子集 (opens new window)区别就是集合里有重复元素了,而且求取的子集要去重。
那么关于回溯算法中的去重问题,在40.组合总和II (opens new window)中已经详细讲解过了,和本题是一个套路。
剧透一下,后期要讲解的排列问题里去重也是这个套路,所以理解“树层去重”和“树枝去重”非常重要。
子集问题去重一定要排序。
class Solution {
public:
vector<int> path;
vector<vector<int>> res;
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
sort(nums.begin(), nums.end()); //去重需要排序!!!!!!
backtracking(nums, 0);
return res;
}
void backtracking(vector<int>& nums, int start_index){
res.push_back(path);
for(int i = start_index; i < nums.size(); i++){
//树层去重,要对同一树层使用过的元素进行跳过
if(i > start_index && nums[i] == nums[i-1]) continue;
path.push_back(nums[i]);
backtracking(nums, i + 1);
path.pop_back();
}
}
};