- 教程
- 文档
【免费下载链接】LogicStack-LeetCode
公众号「宫水三叶的刷题日记」刷穿 LeetCode 系列文章源码
本篇是「宫水三叶的刷题日记」刷穿 LeetCode 系列第 No.1108 篇题解的深度展开,聚焦 LeetCode 1108「IP 地址无效化」这一道难度为简单、Tag 为「模拟」的字符串处理题。文章将以 LeetCode/1101-1110/1108. IP 地址无效化(简单).md 为骨架,完整讲解题意、模拟思路与 Java 实现,并补充 C++/Python/TypeScript 等价写法、复杂度分析与同类题目对照,帮助你真正掌握「字符串逐字符扫描 + 结果构建」这一类模拟题的通用解法。
题目描述与题意理解
原题背景
这是 LeetCode 上的1108. IP 地址无效化(难度:简单),Tag 为「模拟」。
题目定义:给你一个有效的
IPv4地址address,返回这个IP地址的无效化版本。所谓无效化IP地址,其实就是用"[.]"代替了每个"."。
题目给出的唯一提示是:
- 给出的
address是一个有效的IPv4地址。
这意味着输入无需再做格式校验,例如不需要判断段位是否在0~255、是否有前导零等,直接把注意力集中在「替换点号」这一个动作上即可,这正是模拟题「把规则翻译成代码」的典型形态。
示例拆解
示例 1:
输入:address = "1.1.1.1" 输出:"1[.]1[.]1[.]1"- 输入是
1.1.1.1,其中包含 3 个.,逐一替换为[.]后得到1[.]1[.]1[.]1。注意原字符串长度为 7,替换后长度变为7 + 3×2 = 13(每个.由 1 个字符膨胀为 3 个字符,净增 2 个字符)。
示例 2:
输入:address = "255.100.50.0" 输出:"255[.]100[.]50[.]0"- 输入
255.100.50.0同样是 3 个点号,替换后各段数字保持不变,仅点号被包裹进方括号。
这两个示例足以覆盖题目的全部行为:非点号字符原样保留,点号字符被[.]整体替换。
思路分析:为什么这是一道「模拟」题
「模拟」是算法题中非常常见的一类:题目本身不要求你设计精巧的数据结构或复杂的推导,而是把现实生活中(或题目设定的规则里)的某个过程,用代码一步一步「照着做」。本题的规则只有一条——遇到.就替换成[.],其余字符照抄,因此完全符合模拟题的特征。
在 Index/模拟.md 这份模拟类题目的总索引中,本题与 1678. 设计 Goal 解析器(简单)、1047. 删除字符串中的所有相邻重复项(简单)、1410. HTML 实体解析器(中等) 等一同被归类为「字符串模拟」的范畴。它们的共同点是:
- 输入规模小:本题目未给出显式长度上限,但依据有效的 IPv4 地址定义,输入最长不超过 15 个字符(
255.255.255.255),O(n) 与 O(n²) 的差异在此规模下几乎无感; - 规则确定、无分支博弈:不存在贪心选择或状态转移,每一步如何处理完全由当前字符决定;
- 结果是一个新串:需要在遍历过程中持续「构建」输出字符串。
对比同目录下的其他题目可见,1104. 二叉树寻路 与 1106. 解析布尔表达式 分别考察「模拟 + 数学找规律」与「模拟 + 栈」,而本题只考察最纯粹的字符替换模拟,是入门模拟类问题的最佳例题之一。
模拟解法:单遍扫描 + 结果构建
算法流程
根据题意进行模拟即可,核心步骤只有三步:
- 从左到右遍历输入字符串
s的每一个字符; - 若当前字符不是
.,直接将其追加到结果字符串; - 若当前字符是
.,改为追加"[.]"这一整体(先追加[,再追加.,最后追加])。
Java 实现(原题解代码)
原文档给出的 Java 代码如下,这里补充了逐行注释便于理解:
class Solution { public String defangIPaddr(String s) { StringBuilder sb = new StringBuilder(); // 结果构建器 int n = s.length(), idx = -1; // n:输入长度;idx 从 -1 起 while (++idx < n) { // 每次进入循环前先自增,等价于 for (idx = 0; idx < n; idx++) char c = s.charAt(idx); // 取出当前字符 if (c == '.') sb.append('['); // 遇到点号,先补左括号 sb.append(c); // 无论如何都追加当前字符(点号也会被追加进来) if (c == '.') sb.append(']'); // 遇到点号,再补右括号 } return sb.toString(); // 返回构建完成的字符串 } }这段代码的巧妙之处在于:没有使用「遇到.则跳过并追加"[.]"」的分支写法,而是用「前后各补一个括号」的方式,让.字符本身仍然被追加进结果,从而把[、.、]三个字符拼接到位。这样代码中唯一的判断条件仍然是c == '.',逻辑非常紧凑。
等价的多语言实现
原题解以 Java 给出,这里依据完全相同的「逐字符扫描 + 结果构建」思路,整理出其余常用语言的等价实现,可直接在对应语言环境中运行验证:
C++:
class Solution { public: string defangIPaddr(string s) { string ans; for (char c : s) { if (c == '.') ans += "[.]"; else ans += c; } return ans; } };Python:
class Solution: def defangIPaddr(self, address: str) -> str: ans = [] for c in address: if c == '.': ans.append("[.]") else: ans.append(c) return "".join(ans)TypeScript:
function defangIPaddr(address: string): string { let ans = '' for (const c of address) { if (c === '.') ans += '[.]' else ans += c } return ans }说明:以上多语言版本均为依据原题解「模拟」思路整理出的等价实现,仓库本体以 Markdown 题解文档为主体(例如 README.md 所述,这是一个「日更」的题解仓库),其中并不包含独立的源代码文件,提交时请以各语言平台的语法为准。
正确性论证与边界情况
- 无点号输入:例如输入
"1"或"12",循环中不会触发任何一次.分支,结果与输入完全一致,符合「无效化版本」的语义; - 多连续点号:IPv4 规范中不允许连续点号(如
1..1不是有效地址),题目已保证输入有效,因此无需考虑该情况,但即使出现,上述逻辑也会把每个.独立替换为[.],行为依然确定; - 点号位于首尾:有效 IPv4 地址不会以
.开头或结尾,不过即便出现,逻辑同样正确处理,因为算法不依赖点号的位置; - 空串:有效 IPv4 地址非空,但若传入空串,
while循环一次都不进入,直接返回空串,代码同样健壮。
复杂度分析
- 时间复杂度:O(n)。其中 n 为输入字符串长度。整个算法只对输入做一遍扫描,每次字符操作(追加)都是常数时间,因此总耗时与输入规模线性相关。
- 空间复杂度:O(n)。结果字符串的长度为
n + 2×k(k 为点号个数,每个点号由 1 个字符膨胀为 3 个字符),因此构建结果所需的空间与输入长度同阶。若把返回结果本身不计入额外空间,则辅助空间为 O(1)。
从本题延伸:字符串「构建」的性能细节
为什么使用 StringBuilder 而非 String 拼接
在 Java 中,String是不可变对象,str += c每次拼接都会创建新的字符串对象。虽然现代 JVM 与编译器会在简单场景下做优化,但在循环体内反复拼接时,显式使用StringBuilder仍是更稳妥、可控的做法——这正是原题解使用StringBuilder的原因。同理,Python 版本优先使用list收集字符再"".join(ans),而不是在循环里做ans += c的字符串累加,也是为了避免产生大量中间字符串。
为什么不直接调用 replace
String.replace(".", "[.]")一行也能完成替换,且同样是线性复杂度。原题解选择手写循环的价值在于:
- 可读性与教学性:展示模拟题「逐字符处理」的通用框架,这个框架可以直接迁移到 1410. HTML 实体解析器(需要识别
"、&等多字符实体)或 1678. 设计 Goal 解析器(需要按G、()、(al)分段解释)这类「多模式匹配」的字符串模拟题上; - 控制力更强:当替换规则从「单字符 → 固定串」升级为「多字符 → 变长串」时,手写扫描仍然成立,而简单的
replace可能引入重叠匹配等隐患。
扩展到「多模式替换」的通用框架
把本题的扫描框架稍作泛化,就得到字符串模拟题的通用骨架:
初始化结果容器 while (未遍历完输入) { 判断当前位置是否命中某条替换规则 命中:追加替换结果,指针按规则长度前进 未命中:追加原字符,指针前进 1 } 返回结果- HTML 实体解析器 正是这个框架的进阶版:它以
&为触发点向后最多读取 6 个字符,在哈希表中查找匹配的实体再替换,时间复杂度为 O(n×6),与本题的 O(n) 一脉相承。建议将这两道题放在一起练习,可以完整覆盖「简单替换」到「实体解析」的模拟能力梯度。
小结与仓库导航
1108. IP 地址无效化是一道教科书级的「模拟」入门题:规则单一、无陷阱、无优化难点,核心价值在于帮助建立「读题 → 翻译规则 → 逐字符模拟 → 构建结果」的解题习惯。本题的标准解法时间复杂度为 O(n),空间复杂度为 O(n)。
如果你想继续系统性地刷「模拟」类题目,可以从仓库的 Index/模拟.md 索引表入手,按推荐指数由高到低展开;也可以直接浏览 LeetCode/1101-1110/ 目录,与本题同区间还收录了 1104. 二叉树寻路(中等)、1106. 解析布尔表达式(困难)、1109. 航班预订统计(中等) 等不同难度的模拟类题解。这套题解系列从 2021/01/01 开始日更,目标是逐步刷完当时 LeetCode 上所有不带锁的题目,每一篇都力求给出最简洁的代码与清晰的思路讲解。
- 教程
- 文档
【免费下载链接】LogicStack-LeetCode
公众号「宫水三叶的刷题日记」刷穿 LeetCode 系列文章源码
相关推荐
LeetCode-Go 题解 1108:Defanging an IP Address —— Go 实现 IP 地址无效化("." 转 "[.]")
LeetCode Go 题解 1108:Defanging an IP Address —— Go 实现 IP 地址无效化("." 转 " . ") 本篇基于
示例工程字符串哈希全解:从滚动哈希原理到 LeetCode 重复子串/连接词/字符串轮转实战(LogicStack-LeetCode 刷穿系列)
字符串哈希全解:从滚动哈希原理到 LeetCode 重复子串/连接词/字符串轮转实战(LogicStack LeetCode 刷穿系列) 字符串哈希(Strin
教程文档安装 .NET SDK 完整指南:dotnet SDK 10 LTS 一次装对的四条路线
安装 .NET SDK 完整指南:dotnet SDK 10 LTS 一次装对的四条路线 给新机器安装 .NET SDK,最容易翻车的是选错包、选错版本、漏装系
教程文档
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考