难度中等1163
有效 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 <= 20s仅由数字组成
解题思路:回溯
这道题比起 131. 分割回文串 来说要复杂一点,确切要说是其进阶版,我们不止要分割这个串,还得判断这个串是否合法,并且其中对 ‘.’ 号还得尤其注意,不然容易出错!
切割问题可以抽象为树型结构,如图:

接下来就是回溯三部曲啦:
- 递归函数的参数
- 一个 result 数组用来存放最后的结果集,这里还用了一个 tmp 字符串,用来保存每次递归搜索时候的子串,并且我们回溯的时候需要对它进行处理!
- 函数中的参数最重要的就是 index 和 pointNum!index 的话不用说,很重要,因为我们需要去分割,所以每次递归要根据 index 来确定,而 pointNum 用来确定终止条件,因为这道题的 IP 地址中,总共就是 3 个点,那么到达 3 个点的时候,就是我们需要终止的时候!
- 递归终止条件
- 这道题如果我们是以 index >= s.size() 为终止条件的话,那么最后就会导致分割很多点,而这道题最固定的就是这三个分割点,所以终止条件设为分割点的次数才是合理的。当分割点达到 3 个的时候,也就是出现了 3 个时候,我们就需要开始终止递归了!
- 单层搜索逻辑
- 每次我们选去截取当前的子串,也就是 [index, i] ,看看是否符合要求,如果不符合比如说超出了 255 的范围,那么就直接 break,因为前面的子串不符合,那么后面就没意义了!
- 如果符合的话,我们就先将这个子串尾插到 tmp 后,并且将最重要的 ‘.’ 也要尾插上去!(尾插效率不低的!)
- 接着就是递归,继续判断下一个切割位置!
- 最后回溯的时候,我们要将最后的 ‘.’ 去掉,然后再去掉尾部那部分上一次添加的子串(不一定要去掉,使用截取前部分更容易!)
结合下面代码,注意截取子串时候的细节:
class Solution {
public:
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;
}
void backtracking(const string& s, int index, int pointNum)
{
// 若出现了三个点了,则判断一下最后这个子串是否合法
if(pointNum == 3)
{
// 合法的话将其尾插到结果集中
// 注意这里tmp不能使用+=操作符来尾插,只能用+号,不然得再erase最后这个子串部分
if(isValid(s, index, s.size() - 1))
result.push_back(tmp + s.substr(index, s.size() - index + 1));
return;
}
for(int i = index; i < s.size(); ++i)
{
// 不合法说明后面子串都不合格,直接break
if(isValid(s, index, i) == false)
break;
tmp += s.substr(index, i - index + 1); // 处理
tmp.push_back('.'); // 处理
backtracking(s, i + 1, pointNum + 1); // 递归
tmp.pop_back(); // 回溯
tmp.erase(tmp.size() - (i - index + 1)); // 回溯
}
}
vector<string> restoreIpAddresses(string s) {
if (s.size() < 4 || s.size() > 12) // 相当于剪枝
return result;
backtracking(s, 0, 0);
return result;
}
private:
string tmp; // 存放当前IP的字符串
vector<string> result; // 存放结果集
};