题目
题目链接: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 地址。
给定一个只包含数字的字符串 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”]
提示:
- 1 <= s.length <= 20
s 仅由数字组成
解题思路
👉回溯
这题和昨天的分割字符串很像,只是一些条件不同而已。题目是用 '.' 来分割的,而且分割后的字符串必须是有效的 ip 地址。而且对于分割后的字符串和原字符串相比只能多出三个点,其他的不能少也不能多。
读完题应该就能知道需要用递归写,所以就往递归上想。递归的深度应该是字符串的长度,当无法分割字符串时就必须结束递归,但是分割位置不能在字符串的末尾,那样就不符合题意了,所以递归的深度可以用分割次数。因为一个有效的 ip 地址只有三个点,所以只要字符串中存在了三个点,那么这个字符串就已经分割完成。递归的宽度应该很明显了,就是剩余未分割字符串的长度。
**那么该如何分割呢?**既然深度和宽度都已经知道了,那就思考如何利用这两个条件。深度很显然是用来判断递归的结束条件的。那么宽度应该就是用来将未分割的字符串遍历,寻找下一个正确的分割位置。**如何寻找?**将当前分割的字符串拿出来判断是否小于 256 ,且没有前导零。如果满足则在该字符串后面加上点后进入下一层递归。
- 递归函数的参数:原字符串(用来分割),新字符串(存储分割后的字符串),已分割的字符串的位置(下一次分割需要从当前位置开始),已经加入点的个数(分割三次就可以结束)
- 递归函数的返回条件:分割三次和已经把字符串分割完了,分割三次后判断剩余字符串是否满足条件,满足就放入数组中
- 递归函数的功能:遍历剩余字符串寻找合适的分割位置,进行分割
对于判断字符串是否小于 255 的方法有:
-
stoi(string),这个函数可以直接将 string 转为 int 型,头文件是 <string> 。这个函数也会进行强制转换,而且字符串的范围不能超过 int 的范围,否则会报错。这个函数是C++11的特性 -
判断函数,还可以直接写一个判断函数,函数里用循环遍历的方法把字符串转为数字 bool isip(string sb) {
int n = sb.size();
if (n == 1)
return true;
if (n == 0 || sb[0] == '0' || n > 3)
return false;
if (n == 2)
return true;
int b = 0;
for (int i = 0; i < 3; ++ i) {
int a = sb[i] - '0';
b = b * 10 + a;
}
if (b > 255)
return false;
return true;
}
代码(C++)
回溯
class Solution {
public:
vector<string> str;
void dfs(string s, int n, string res, int m) {
if (m == s.size())
return ;
if (n == 3) {
string t = s.substr(m);
if (t.size() > 3 || (t.size() > 1 && t[0] == '0'))
return ;
int x = stoi(t);
if (x <= 255) {
res += t;
str.push_back(res);
}
return ;
}
string ans;
for (int i = m; i < s.size(); ++ i) {
ans += s[i];
if (ans.size() > 3)
return ;
if (ans.size() > 1 && ans[0] == '0')
return ;
int j = stoi(ans);
if (j <= 255) {
dfs(s, n + 1, res + ans + ".", i + 1);
}
}
}
vector<string> restoreIpAddresses(string s) {
dfs(s, 0, "", 0);
return str;
}
};
总结
这题如果仔细去想的话不算太难,只是在一个字符串上面寻找插入点,然后再判断插入点之间的字符串是否满足题目给的条件。这样想的话可以用拆分法把判断是否满足条件写入一个函数里,再用递归遍历插入点,这样好像就简单点。所以很多难的题目都是由一些简单的问题堆积而成的,只要慢慢想,逐个分解就能解决。
|