1、93复原IP地址

有效 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 地址。
示例 1:

  • 输入:s = “25525511135”
  • 输出:[“255.255.11.135”,“255.255.111.35”]

我的思路131分割回文串类似,这道题是分割IP地址。不是回文串的就要剪枝,这题同样不是有效的IP元素的也要剪枝。先写出多叉树,再写递归逻辑。遇到回溯的题先在纸上把问题画为多叉树演化一遍,不然很多细节想不到。

代码随想录:在原串上插入"."来分割,不新建串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// 我的
class Solution {
public:
void traverse(string &s,int startidx, int depth, string &path, vector<string> &res){
if(startidx == s.size()){ // 截止条件,搜索完IP
if(depth == 5){ // 检查IP是否四个段
res.emplace_back(path.substr(0,path.size()-1));// remove "."
}
return;
}

for(int i = startidx;i<s.size();i++){
if(depth == 4){
i = s.size()-1;
}
if(!isIPItem(s,startidx,i))continue; // 剪枝
int sub_size = i-startidx+1;
path.append(s,startidx,sub_size);
path+=".";
traverse(s,i+1,depth+1,path,res);
path.pop_back(); // remove "."
path.erase(path.size()-sub_size,sub_size);
}
return;
}
bool isIPItem(string &s,int start, int end){
if(start != end && s[start]=='0')return false; // 0x 剪枝
if((end-start)>2)return false; //剪枝,最大范围255为三位
cout << end-start <<endl;
int ipitem = std::stoi(s.substr(start,end-start+1));
if(0 > ipitem || ipitem > 255)return false;
return true;
}
vector<string> restoreIpAddresses(string s) {
string path;
vector<string> res;
traverse(s,0,1,path,res);
return res;
}
};
// 代码随想录
class Solution {
private:
vector<string> result;// 记录结果
// startIndex: 搜索的起始位置,pointNum:添加逗点的数量
void backtracking(string& s, int startIndex, int pointNum) {
if (pointNum == 3) { // 逗点数量为3时,分隔结束
// 判断第四段子字符串是否合法,如果合法就放进result中
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)) { // 判断 [startIndex,i] 这个区间的子串是否合法
s.insert(s.begin() + i + 1 , '.'); // 在i的后面插入一个逗点
pointNum++;
backtracking(s, i + 2, pointNum); // 插入逗点之后下一个子串的起始位置为i+2
pointNum--; // 回溯
s.erase(s.begin() + i + 1); // 回溯删掉逗点
} else break; // 不合法,直接结束本层循环
}
}
// 判断字符串s在左闭右闭区间[start, end]所组成的数字是否合法
bool isValid(const string& s, int start, int end) {
if (start > end) {
return false;
}
if (s[start] == '0' && start != end) { // 0开头的数字不合法
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) { // 如果大于255了不合法
return false;
}
}
return true;
}
public:
vector<string> restoreIpAddresses(string s) {
result.clear();
if (s.size() < 4 || s.size() > 12) return result; // 算是剪枝了
backtracking(s, 0, 0);
return result;
}
};

2、78子集

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的
子集(幂集)。解集不能包含重复的子集。你可以按 任意顺序 返回解集。

我的思路:典型的组合问题。由于组合的个数没有限制,所以不需要往后剪枝。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
void traverse(vector<int>& nums,int startidx,vector<int> &path,vector<vector<int>> &res){
res.emplace_back(path);
if(startidx == nums.size())return;

for(int i = startidx;i < nums.size();i++){
path.emplace_back(nums[i]);
traverse(nums,i+1,path,res);
path.pop_back();
}
}
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> res;
vector<int> path;
traverse(nums,0,path,res);
return res;
}
};

3、90子集II

给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的
子集(幂集)。解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

我的思路:与78子集不同,这题的候选元素有重复。候选元素重复要考虑树层去重树枝去重两种情况。这里单个子集中的元素可以重复,例如[1,2,2],实际上两个2来自两个不同的元素。所以这题只用树层去重

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public:
void traverse(vector<int>& nums, int startidx, vector<bool> &used, vector<int> &path,vector<vector<int>> &res){
res.emplace_back(path);
if(startidx >= nums.size())return;

for(int i = startidx;i<nums.size();i++){
if(i > 0 && nums[i-1]==nums[i] && used[i-1]==false)continue;
path.emplace_back(nums[i]);
used[i] = true;
traverse(nums,i+1,used,path,res);
used[i] = false;
path.pop_back();
}
}

vector<vector<int>> subsetsWithDup(vector<int>& nums) {
vector<int> path;
vector<vector<int>> res;
vector<bool> used(nums.size(),false);

std::sort(nums.begin(),nums.end());
traverse(nums,0,used,path,res);
return res;
}
};