BM74 数字字符串转化成IP地址
知识点字符串回溯
描述
现在有一个只包含数字的字符串,将该字符串转化成IP地址的形式,返回所有可能的情况。例如:给出的字符串为"25525522135",返回["255.255.22.135", "255.255.221.35"]. (顺序没有关系)
数据范围:字符串长度 要求:空间复杂度
,时间复杂度
注意:ip地址是由四段数字组成的数字序列,格式如 "x.x.x.x",其中 x 的范围应当是 [0,255]。
示例1
输入:
"25525522135"
复制返回值:
["255.255.22.135","255.255.221.35"]
复制
示例2
输入:
"1111"
复制返回值:
["1.1.1.1"]
复制
示例3
输入:
"000256"
复制返回值:
"[]"
题解
递归+回溯解法
一个ip由4个16bit的二进制数组成,我们称每一个16bit的数字为一个分节。每个分节的取值范围是0~255,其长度使用字符串表示最短为1个字节,最长为3个。对于任意的一个分节,我们每次可以从输入数据中选取1~3个字节之后,再选取下一分解。
我们可以实现一个函数用于求解从索引index开始,处于第counter个分解的所有ip的情况。步骤如下:
- 开始条件:索引index从0开始,counter也从0开始表示还没有选取分解
- 本层实现:在选取第counter个分解的时候只要剩余的数据足够长我们都可以选取1~3个字符,假设我们选取k个字符,那么进入下一层的索引index = index + k,counter+=1,ip = ip + s.sub_str(index,k)。限制条件:所选取字符不能有前导0,他们的值不能大于255。
- 终止条件:如果counter等于4,表示选取到最后一个,如果刚好走完整个数组则表示一个合法的ip
代码如下:
using namespace std;
// BM74 数字字符串转化成IP地址
// https://www.nowcoder.com/practice/ce73540d47374dbe85b3125f57727e1e?tpId=295&tqId=653&ru=/exam/oj&qru=/ta/format-top101/question-ranking&sourceUrl=%2Fexam%2Foj%3Fpage%3D1%26tab%3D%25E7%25AE%2597%25E6%25B3%2595%25E7%25AF%2587%26topicId%3D295
int append_str(std::string &ip, const std::string &s, int start, int len)
{
if (start + len > s.size())
{
return 0;
}
int res = len;
if (!ip.empty())
{
ip.append(1, '.');
res++;
}
ip.append(s.substr(start, len));
return res;
}
void pop_str(std::string &ip, int count)
{
for (int i = 0; i < count; ++i)
{
ip.pop_back();
}
}
void solve(const std::string &s, int index, std::string &ip, int counter, std::vector<std::string> &res)
{
// 非法的条件~~
if (counter > 4 || index > s.size() || (counter == 4 && index < s.size() - 1))
{
return;
}
// 终止条件,此时将ip放入结果队列中
if (index == s.size() && counter == 4)
{
res.push_back(ip);
return;
}
for (int k = 0; k < 3; ++k)// 每次可以选择1~3个子杰
{
if (index + k >= s.size())// 防止越界
{
break;
}
if (k > 0 && s[index] == '0')// 如果选择1个以上字节的时候,不能有前导0
{
break;
}
if (k == 2)// 防止大于255的数据坐无畏的递归
{
int value = 0;
for (int i = index; i <= index + k; ++i)
{
value = value * 10 + (s[i] - '0');
}
if (value > 255)
{
continue;
}
}
int len = append_str(ip, s, index, k + 1);// 将当前的字符放入ip中
solve(s, index + k + 1, ip, counter + 1, res);
pop_str(ip, len);// 回溯
}
}
vector<string> restoreIpAddresses(string s)
{
std::string ip;
int counter{0};
int num{0};
int index{0};
std::vector<std::string> res;
solve(s, index, ip, counter, res);
return res;
}
int main()
{
std::vector<std::string> cases = {
"25525522135"};
for (auto &s : cases)
{
auto res = restoreIpAddresses(s);
for (auto &ip : res)
{
std::cout << ip << std::endl;
}
}
return 0;
}
暴力求解法
我们可以把求解看做在这个字符串中插入3个'.'号。用一个3层的for循环分别表示插入第一、第二、第三个点的位置
具体做法:
- 依次枚举这三个点的位置。
- 然后截取出四段数字。
- 比较截取出来的数字,不能大于255,且除了0以外不能有前导0,然后才能组装成IP地址加入答案中。
代码略~