题目:
给定一个只包含数字的字符串,用以表示一个 IP 地址,返回所有可能从 s 获得的 有效 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 地址。
/****************************解法一::一种新思路***********************************/
// 这个方法是根据 起始为 4 ,逐次递减,如果次数减到 0 ,那就说明取够4个段了
// 这个题,把点和数字放一块处理的话,容易踩坑上当
class Solution
{
public:
vector<string> restoreIpAddresses(string s)
{
// 注意为什么是 4 ,因为IP地址就四个段
backtracking(s, 0, 4);
return result;
}
private:
vector<string> strs; // 存放有效地IP
vector<string> result; // 存放最终的结果
// count 用来计每一层的分支个数,每层至多有4个分支
void backtracking(string& s, int startIndex, int count)
{
// 这里写终止逻辑
if(startIndex == s.size() && count == 0)
{
// 当处理完最后一个,且都符合IP地址的定义,在添加小数点
string str;
for(auto it : strs)
{
str += it;
str += '.';
}
// 这里 substr() 也很妙啊
result.push_back(str.substr(0, str.size() - 1));
return;
}
if(startIndex == s.size() || count <= 0)
{
return;
}
int num = 0;
// (int)s.size() 这里 因为size的类型是 size_t ,直接用 min可能会出错
// 这里 startIndex+3 的逻辑,与 count 其实是在做一件事儿嘛,
// 实时证明,他们两个缺一不可
// XXXXXXX 1 刚刚这里错写成了 startIndex+3 ,整个没有理解到位
for(int i = startIndex; i < min((int)s.size(), startIndex+3); ++i)
{
// 这个方法把判断所切割的字符串是否合法与回溯放一起了
// XXXXXXX 2 这个里面的也写错了,应该是startIndex,错写成 i 了
if(i > startIndex && s[startIndex] == '0') return;
if(s[i] > '9' || s[i] < '0') return;
num = 10 * num + (s[i] - '0');
if(num > 255) return;
// 上面四部用来判断是否合法
strs.push_back(s.substr(startIndex, i - startIndex + 1));
// XXXXXXXX 3 这个里面也写错了,把 i 错写成了 startIndex
backtracking(s, i + 1, count -1); // count 这里也隐藏了回溯
strs.pop_back();
}
}
};
/****************************解法二::正常套路***********************************/
// 这个方法里判断一层至多有4个是分支,是通过对小数点的计数 count 实现的
// class Solution {
// private:
// vector<string> result;
// // string str; // 存储符合条件的集合,不合条件直接就舍弃,没有保存
// // 函数一:检测所切割出来的字符串是否符合
// bool isConform(string& s, int start, int end)
// {
// // 少一个判断
// if(start > end)
// {
// return false;
// }
// // 前置有0应该写在这里
// if(s[start] == '0' && start != end)
// {
// return false;
// }
// int num = 0;
// for(int i = start; i <= end; i++)
// {
// // // 1、前置有0
// // if(i == start && s[i] == 0 && s[i+1] == 0)
// // {
// // return false;
// // }
// // 2、特殊字符
// if(s[i] < '0' || s[i] > '9')
// {
// return false;
// }
// num = num * 10 + (s[i] - '0');
// // 3、不在 0-255的范围
// // 这个也写错了,应该放在for循环里面,我放在外面了
// if(num > 255)
// {
// return false;
// }
// }
// return true;
// }
// // 函数二:回溯函数
// void backtracking(string& s, int startIndex, int count)
// {
// // IP地址有4个数组成,所以最多有4层递归,那么怎么控制呢?
// // 通过记录小数点的个数 count
// // ??????????? 这里应该是几呢?
// if(count == 3)
// {
// // 刚刚的疑问看了答案明白了,因为最后还需要判断第三个小数点之后取得的 str 是否符合,所以这里还有一次判断
// // 注意::这里最后一个值,肯定是到最后一位的哦!!!刚刚也没想到
// // 所以这个就是判断第四段是否合法
// if(isConform(s, startIndex, s.size() - 1))
// {
// result.push_back(s);
// }
// return;
// }
// // 单层逻辑
// for(int i = startIndex; i < s.size(); i++)
// {
// if(isConform(s, startIndex, i))
// {
// s.insert(s.begin() + i + 1, '.'); // 在i后面加一个小数点
// count++;
// backtracking(s, i + 2, count);
// s.erase(s.begin() + i + 1); // 删掉小数点,属于回溯的过程
// count--;
// }
// else
// {
// // 不合法直接结束本层循环,这里可不是 continue 哦,我写错了~~~~
// break;
// }
// }
// }
// public:
// vector<string> restoreIpAddresses(string s)
// {
// if(s.size() < 4 || s.size() > 12)
// {
// return result;
// }
// int count = 0;
// backtracking(s, 0, count);
// return result;
// }
// };
/************************错误***********错误********错误*************************/
/************************错误***********错误********错误*************************/
/************************错误***********错误********错误*************************/
/******时间紧任务重,放弃这种做法吧~,没有AC成功的代码任由他去吧,回头有空再看看********/
// class Solution {
// private:
// vector<string> result;
// string str; // 存储符合条件的集合,不合条件直接就舍弃,没有保存
// // 函数一:检测所切割出来的字符串是否符合
// bool isConform(string& s, int start, int end)
// {
// // 少一个判断
// if(start > end)
// {
// return false;
// }
// // 前置有0,这个是放在for循环外面,且这个逻辑要记忆一下呢,自己没想到
// if(s[start] == 0 && start != end)
// {
// return false;
// }
// int num = 0;
// for(int i = start; i < end; i++)
// {
// // 2、特殊字符
// if(s[i] < '0' || s[i] > '9')
// {
// return false;
// }
// num = num * 10 + (s[i] - '0');
// // 3、不在 0-255的范围,我自己把这个写在for 外面了
// // 也需要想想清楚,为什么是写在for循环里面,因为在运算的过程中,一旦出现了大于255,继续加肯定还是大于255
// // 避免了不必要的运算过程
// if(num > 255)
// {
// return false;
// }
// }
// return true;
// }
// // 函数二:回溯函数
// void backtracking(string& s, int startIndex, int count)
// {
// // IP地址有4个数组成,所以最多有4层递归,那么怎么控制呢?
// // 通过记录小数点的个数 count
// // ??????????? 这里应该是几呢?
// if(count == 3)
// {
// // 刚刚的疑问看了答案明白了,因为最后还需要判断第三个小数点之后取得的 str 是否符合,所以这里还有一次判断
// // 注意::这里最后一个值,肯定是到最后一位的哦!!!刚刚也没想到
// if(isConform(s, startIndex, s.size() - 1))
// {
// str += s.substr(startIndex, s.size() - 1);
// result.push_back(str);
// str.pop_back;
// }
// str.clear();
// return;
// }
// // 单层逻辑
// for(int i = startIndex; i < s.size(); i++)
// {
// if(isConform(s, startIndex, i))
// {
// str += s.substr(startIndex, i - startIndex + 1);
// str += '.';
// count++;
// backtracking(s, i + 1, count);
// str.pop_back();
// count--;
// }
// else
// {
// // 不合法直接结束本层循环,这里可不是 continue 哦,我写错了~~~~
// break;
// }
// }
// }
// public:
// vector<string> restoreIpAddresses(string s)
// {
// if(s.size() < 4 || s.size() > 12)
// {
// return result;
// }
// int count = 0;
// backtracking(s, 0, count);
// return result;
// }
// };
本文分析了一种新思路和传统方法来解决IP地址恢复问题,通过递归和计数小数点控制分支,讲解了如何判断有效IP并进行回溯。两种解法的优缺点及常见错误分析有助于理解IP地址验证的逻辑。
232

被折叠的 条评论
为什么被折叠?



