前言
容易确定本题采用回溯法,挑战在于对各种情况做分类以及全面考虑一些特殊情况。
一、解题思路
回溯函数传入的参数包括:
- 初始字符串s
- s的当前索引位置index
- 记录IP中某段当前累计的字符串cur
- 用于存放IP中四个字段的vector<string>类型的容器combination
回溯函数中所有情况分类如下:
- 字符串s中所有字符用尽即index=s.size(),检查combination的大小
1)combination.size() = 4找到一组满足要求的IP组合,记录结果并返回
2)combination.size() != 4函数直接返回 - 若当前字段容器大小等于4但字符串中字符还未用尽,一定不符合要求,函数直接返回,即combination.size() == 4 && index<s.size()
- 累计字符串cur的长度不会超过3,当长度超过3时不符合要求,函数直接返回,即cur.size() > 3
- 当前位置index对应的字符可能有两种组合方式
1)与当前段cur组合在一起作为IP地址中的同一段:将s[index]加入cur分段中,进入递归函数
2)作为下一段的起始字符:需要将当前cur分段检验(是否位于0到255之间,且不含有前导0)后,压入combination容器后清空cur分段,再将s[index]加入新空白cur分段中。
二、代码实现
class Solution {
public:
vector<string> res;
// 检验函数:检验分段是否满足要求(1)值位于0~255之间 (2)没有前导0
//注意:该函数传入的cur不能为空字符串,需要在传入函数之前进行判断
bool check(string cur){
int val = stoi(cur);
if(0 <= val && val <= 255){
if(cur[0] == '0' && cur.size() > 1) return false;
return true;
}
return false;
}
//回溯函数
void backtrace(string& s, int index, string cur, vector<string> combination){
if(index == s.size()){
//字符串遍历结束判断当前cur是否符合要求,若符合要求则压入分段记录容器中
if(!cur.empty() && check(cur)){
combination.push_back(cur);
cur.clear();
}
//找到一组IP地址,还原为字符串形式压入结果记录容器res中
if(combination.size() == 4 && cur.empty()){
string str;
for(int i = 0; i < 4; ++i) {
str += combination[i];
str.push_back('.');
}
str.pop_back();
res.push_back(str);
}
return;
}
//提前结束回溯的情况
if((combination.size() == 4 && index < s.size()) || cur.size() > 3)
return;
cur.push_back(s[index]);
backtrace(s, index + 1, cur, combination);
cur.pop_back();
if(!cur.empty() && check(cur)) {
combination.emplace_back(cur);
cur.clear();
cur.push_back(s[index]);
backtrace(s, index + 1, cur, combination);
}
}
// 算法入口函数
vector<string> restoreIpAddresses(string s) {
backtrace(s, 0, "", {});
return res;
}
};