Restore IP Addresses

本文介绍了一种通过递归算法解析字符串并返回所有可能的有效IP地址组合的方法。该算法使用回溯技术来验证每部分IP的有效性,并确保最终输出的IP地址符合规范。

  1. 首先我要在纸上,非常非常聪明且迅速且机灵,
  2. 给出几个用例,找出边界用例和特殊用例,确定特判条件;在编码前考虑到所有的条件
  3. 向面试官提问:问题规模,特殊用例
  4. 给出函数头
  5. 暴力解,简述,优化。
  6. 给出能够想到的最优价
  7. 伪代码,同时结合用例
  8. 真实代码

Restore IP Addresses

Given a string containing only digits, restore it by returning all possible valid IP address combinations.

For example:
Given "25525511135",

return ["255.255.11.135", "255.255.111.35"]. (Order does not matter)



class Solution {
public:
    vector<string> restoreIpAddresses(string s) {
    	const char* str = s.c_str();
		vector<string> ret;
		int size = s.size();
		for(int i = 1; i < size; ++i)
		{
			if(!isValid(str, i))
				continue;
			for(int j = i+1; j < size; ++j)
			{
				if(!isValid(str+i, j-i))
					continue;
				for(int k = j+1; k < size; ++k)
				{
					if(!isValid(str+j, k-j))
						continue;
					if(isValid(str+k, size-k))
					{
						ret.push_back(s.substr(0, i) + "." + s.substr(i, j-i) + 
							"." + s.substr(j, k-j) + "." + s.substr(k, size-k));
					}
				}
			}
		}
		return ret;
    }
	bool isValid(const char* s, int len)
	{
		if('0' == s[0])
			if(1 == len)
				return true;
			else
				return false;
		int temp = 0;
		for(int i = 0; i < len; ++i)
		{
			temp = temp*10 + (int)(s[i] - '0');
			if(temp > 255)
				return false;
		}
		if(temp <= 255 && temp >= 1)
			return true;
		else
			return false;
	}
};








Java是一种具备卓越性能与广泛平台适应性的高级程序设计语言,最初由Sun Microsystems(现属Oracle公司)的James Gosling及其团队于1995年正式发布。该语言在设计上追求简洁性、稳定性、可移植性以及并发处理能力,同时具备动态执行特性。其核心特征与显著优点可归纳如下: **平台无关性**:遵循“一次编写,随处运行”的理念,Java编写的程序能够在多种操作系统与硬件环境中执行,无需针对不同平台进行修改。这一特性主要依赖于Java虚拟机(JVM)的实现,JVM作为程序与底层系统之间的中间层,负责解释并执行编译后的字节码。 **面向对象范式**:Java全面贯彻面向对象的设计原则,提供对封装、继承、多态等机制的完整支持。这种设计方式有助于构建结构清晰、模块独立的代码,提升软件的可维护性与扩展性。 **并发编程支持**:语言层面集成了多线程处理能力,允许开发者构建能够同时执行多项任务的应用程序。这一特性尤其适用于需要高并发处理的场景,例如服务器端软件、网络服务及大规模分布式系统。 **自动内存管理**:通过内置的垃圾回收机制,Java运行时环境能够自动识别并释放不再使用的对象所占用的内存空间。这不仅降低了开发者在内存管理方面的工作负担,也有效减少了因手动管理内存可能引发的内存泄漏问题。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
#include <iostream> #include <vector> #include <string> using namespace std; // 手动判断子串是否合法,并转为整数(避免 stoi 和 substr) bool isValidPart(const string& s, int start, int len) { if (len == 0 || len > 3) return false; if (len > 1 && s[start] == '0') return false; // 前导零非法 int num = 0; for (int i = 0; i < len; ++i) { char c = s[start + i]; if (c < '0' || c > '9') return false; // 非数字字符(其实题目保证只有数字) num = num * 10 + (c - '0'); } return num <= 255; } // 获取所有有效 IP 地址(无 substr,纯索引操作) vector<string> restoreIpAddresses(const string& s) { vector<string> result; int n = s.size(); // 剪枝:长度必须在 [4,12] if (n < 4 || n > 12) return result; // 枚举三段长度:l1, l2, l3(每段 1~3) for (int l1 = 1; l1 <= 3; ++l1) { if (l1 >= n) break; for (int l2 = 1; l2 <= 3; ++l2) { if (l1 + l2 >= n) break; for (int l3 = 1; l3 <= 3; ++l3) { int l4 = n - l1 - l2 - l3; if (l4 <= 0 || l4 > 3) continue; // 检查四段合法性 if (!isValidPart(s, 0, l1)) continue; if (!isValidPart(s, l1, l2)) continue; if (!isValidPart(s, l1+l2, l3)) continue; if (!isValidPart(s, l1+l2+l3, l4)) continue; // 手动构造结果字符串(避免多次 string 拼接开销) string ip; ip.reserve(16); // IP 最长如 "255.255.255.255" → 15 字符 // 第一段 for (int i = 0; i < l1; ++i) ip += s[i]; ip += '.'; // 第二段 for (int i = l1; i < l1+l2; ++i) ip += s[i]; ip += '.'; // 第三段 for (int i = l1+l2; i < l1+l2+l3; ++i) ip += s[i]; ip += '.'; // 第四段 for (int i = l1+l2+l3; i < n; ++i) ip += s[i]; result.push_back(ip); } } } return result; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int T; cin >> T; while (T--) { string s; cin >> s; vector<string> ips = restoreIpAddresses(s); if (ips.empty()) { cout << "-1\n"; } else { for (const string& ip : ips) { cout << ip << '\n'; } } } return 0; } 这个好像有点问题
11-16
你写得非常接近完美了,逻辑清晰、优化到位。但你说“**这个好像有点问题**”,说明在实际测试中可能出现了: - ❌ 输出错误(如漏解或非法 IP) - ❌ 多输出或少输出 - ❌ 格式不对(比如 `-1` 没有换行) - ❌ 特殊情况没处理好(例如 `"0000"` 应该是合法的 `0.0.0.0`) --- 我们来 **逐项排查并修复潜在问题**。 --- ## ✅ 问题定位与修复 ### 🔍 问题 1:`"0000"` 必须能生成 `"0.0.0.0"` 你的 `isValidPart` 函数判断: ```cpp if (len > 1 && s[start] == '0') return false; ``` ✅ 这是对的:禁止 `"01"`、`"00"` 等前导零; ❌ 但是当 `len=1` 且字符为 `'0'` 时,是允许的! 所以 `"0"` 是合法段 → `"0.0.0.0"` 可以被正确生成。 ✔️ 测试 `"0000"`: - l1=1, l2=1, l3=1, l4=1 → 四段都是 `"0"` → 合法! ✅ 所以这部分其实没问题。 --- ### 🔍 问题 2:`"010010"` 被错误接受? 比如某一段是 `"010"`,虽然数值上是 10 ≤ 255,但它有前导零且长度 >1 → 应非法。 而你的代码: ```cpp if (len > 1 && s[start] == '0') return false; ``` ✅ 正确拦截了 `"010"`(因为首字符是 `'0'` 且 len=3>1)→ 返回 `false` ✅ 安全。 --- ### 🔍 问题 3:手动拼接字符串是否安全?有没有越界? 看这段: ```cpp for (int i = l1; i < l1+l2; ++i) ip += s[i]; ``` 索引范围 `[l1, l1+l2)` → 共 `l2` 个字符,✅ 正确。 同理其他段也正确。 --- ### 🔍 问题 4:剪枝条件不够精确(关键!) 这是最可能出错的地方: ```cpp if (l1 >= n) break; ... if (l1 + l2 >= n) break; ``` ⚠️ 错误! 你应该保证每一段之后至少还能分出剩下三段(每段至少 1 字符),所以: #### 更严格的剪枝应该是: - 第一段最多取到 `n-3` 位置(后面要留至少 3 字符) - 第二段最多取到 `n-2` 位置(后面留 2 字符) - 第三段最多取到 `n-1` 位置(后面留 1 字符) 但我们用的是 **长度枚举法**,应该改为对 `l1`, `l2`, `l3` 加上限控制。 --- ## ✅ 修复后的完整代码(重点修正边界条件) ```cpp #include <iostream> #include <vector> #include <string> using namespace std; bool isValidPart(const string& s, int start, int len) { if (len == 0 || len > 3) return false; if (len > 1 && s[start] == '0') return false; // 前导零不允许 int num = 0; for (int i = 0; i < len; ++i) { char c = s[start + i]; if (c < '0' || c > '9') return false; num = num * 10 + (c - '0'); } return num <= 255; } vector<string> restoreIpAddresses(const string& s) { vector<string> result; int n = s.size(); // 长度必须在 [4,12] if (n < 4 || n > 12) return result; // 枚举前三段的长度(每段 1~3) for (int l1 = 1; l1 <= 3; ++l1) { if (l1 + 3 > n) continue; // 后面三段至少需要3字符 for (int l2 = 1; l2 <= 3; ++l2) { if (l1 + l2 + 2 > n) continue; // 后面两段至少需要2字符 for (int l3 = 1; l3 <= 3; ++l3) { int l4 = n - l1 - l2 - l3; if (l4 <= 0 || l4 > 3) continue; // 第四段必须合法 // 检查四段合法性 if (!isValidPart(s, 0, l1)) continue; if (!isValidPart(s, l1, l2)) continue; if (!isValidPart(s, l1 + l2, l3)) continue; if (!isValidPart(s, l1 + l2 + l3, l4)) continue; // 构造结果 string ip; ip.reserve(16); // 添加第一段 for (int i = 0; i < l1; ++i) ip += s[i]; ip += '.'; // 第二段 for (int i = l1; i < l1 + l2; ++i) ip += s[i]; ip += '.'; // 第三段 for (int i = l1 + l2; i < l1 + l2 + l3; ++i) ip += s[i]; ip += '.'; // 第四段 for (int i = l1 + l2 + l3; i < n; ++i) ip += s[i]; result.push_back(ip); } } } return result; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int T; cin >> T; while (T--) { string s; cin >> s; vector<string> ips = restoreIpAddresses(s); if (ips.empty()) { cout << "-1\n"; } else { for (const string& ip : ips) { cout << ip << '\n'; } } } return 0; } ``` --- ## ✅ 新增测试用例验证 | 输入 | 期望输出 | |------|----------| | `"0000"` | `0.0.0.0` | | `"25525511135"` | `255.255.11.135`, `255.255.111.35` | | `"010010"` | `0.10.0.10`, `0.100.1.0`? | 👉 `"010010"` 的合法划分有哪些? 尝试: - `0.10.0.10` → ✅ - `0.100.1.0` → ✅ - `0.10.01.0` → ❌ `"01"` 非法 - `0.1.0.10` → ✅ 所以应输出多个。 运行代码验证是否都包含? --- ## ✅ 为什么原版“看似有问题”? 你原来的版本: ```cpp if (l1 >= n) break; if (l1 + l2 >= n) break; ``` 这会导致: - 循环提前 `break`,而不是 `continue`; - 实际上应该用 `continue` 跳过当前组合,而不是 `break` 整个内层循环; 但更重要的是:这些 `break` 条件不准确,可能导致遗漏有效分割! ✅ 所以我们改成了更合理的 `if (...) continue;` 并加上语义明确的剩余长度检查。 --- ## ✅ 总结:你原来的问题在哪? | 原因 | 是否存在 | 说明 | |------|---------|------| | 宏定义冲突导致编译错误 | 可能 | 如果你加了 `#define max_size` | | 分段剪枝不当 | ✅ 存在 | `break` 导致跳过本不该跳过的组合 | | 手动拼接出错 | ❌ 不存在 | 拼接逻辑正确 | | 前导零判断 | ✅ 正确 | 已经防止了 `"01"` 类型 | | 输出格式 | ⚠️ 注意 | 确保每个 IP 单独一行,空结果输出 `-1` | --- ## ✅ 推荐最终风格(简洁+高效) 如果你追求极简,也可以这样写: ```cpp for (int a = 1; a <= 3; a++) for (int b = 1; b <= 3; b++) for (int c = 1; c <= 3; c++) { int d = n - a - b - c; if (d < 1 || d > 3) continue; // 判断四段 if (check(s,0,a) && check(s,a,b) && check(s,a+b,c) && check(s,a+b+c,d)) res.push_back(s.substr(0,a)+'.'+s.substr(a,b)+'.'+s.substr(a+b,c)+'.'+s.substr(a+b+c)); } ``` 虽然用了 `substr`,但在小数据下完全可接受。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值