题目描述
思路解析
一句话答案:LeetCode 1108 IP 地址无效化:把地址字符串里每个小数点 "." 都替换成 "[.]" 三个字符,数字原样照抄——扫一遍逐字拼新串,或直接调内置 replace,时间 O(n)、空间 O(n)。
把 IP 里的点包起来,要返回什么串
给一个合法的 IPv4 地址,也就是形如 a.b.c.d、四段数字用三个点隔开的那种网络地址,要做的只有一件事:把每个小数点 "." 换成 "[.]" 这三个字符,数字和别的内容原封不动,返回换好的新串。题面例子 "1.1.1.1" 变成 "1[.]1[.]1[.]1";本节演示的 "100.2.30.4" 变成 "100[.]2[.]30[.]4"——三个点各鼓成一段方括号,数字一位没变。
就地把点改成方括号,后面下标为什么会乱
最容易踩的一步,是拿着原串从左往右就地替换。麻烦在于 "[.]" 有三个字符、比原来一个点长,把第一个点改完,它右边所有还没处理的字符会被整体往后顶三格,下标全部右移;接着再按老下标去找下一个点,找到的已经不是原来那个位置了。真想在原串上就地改,得反过来从右往左动:已经改过的都堆在右边,左边还没碰的部分下标纹丝不动。
扫一遍另拼一个新串,或一行 replace
更省心的办法是不在原串上动刀:另开一个空结果串,从左到右一个字符一个字符看。看到小数点,就往结果串后面接 "[.]" 三个字符;不是小数点,就把这个字符原样接上去。原串只读不改,新串从空慢慢长出来,下标错乱的问题压根不会发生。
落到实现上更直接——Python 和 Java 都有现成的字符串替换,一行 address.replace(".", "[.]") 就把所有点全换掉、返回新串,连循环都省了。逐字扫描那套,是它内部替你干的活;自己手写多半用来练手,或碰上没有现成替换函数的场合兜底。
"100.2.30.4" 逐字走一遍
结果串从空开始。开头 "1"、"0"、"0" 三个数字都不是点,依次抄进去,结果串成 "100"。第 4 位是小数点,不直接抄、写成 "[.]",结果串变 "100[.]"。第 5 位 "2" 照抄,得 "100[.]2"。第 6 位又是点,补 "[.]",成 "100[.]2[.]"。第 7、8 位 "3"、"0" 抄上,成 "100[.]2[.]30"。第 9 位是第三个点,再补 "[.]",成 "100[.]2[.]30[.]"。最后一位 "4" 照抄,拼出 "100[.]2[.]30[.]4"。原串三个点各换成一段方括号,十个数字字符一个没动,和题面输出对得上。
拿 replaceAll 当正则,点就成了万能符
有个语言层的坑得防:别的符号别跟着换,题目只认小数点 ".",数字和其它字符必须留原样。还有 Java 里 String.replace 收的是字面字符,点就是点、不走正则那套按模式匹配的规则;只有 replaceAll 才按正则解析,那时得先把 "." 转义成 "\.",否则它会当成「匹配任意一个字符」,把整串都换烂。
复杂度上,每个字符只看一遍、决定抄一个还是写三个,时间 O(n),n 是地址长度。空间要另存一个新串,每个点会膨胀成三个字符,新串最长约 3n,仍是 O(n)。地址不管几段、数字几位、末段是不是 0,规则始终一条:是点换成 "[.]",不是点照抄。
▶ 动画逐步走查(文字版)——想跟着上方动画一帧帧对照就展开
记牢两句话:是小数点就写成 "[.]",不是小数点就原样照抄。下面每一帧都在套这两句。
总览 · 准备一个空结果串,从最左边开扫:上面这一排是地址 "100.2.30.4" 拆开的 10 个字符,数字和小数点夹在一起。开扫之前先准备一个空的结果串,等会儿每看一个字符就往里添东西。从最左边第 0 个字符开始,一个一个往右看。
读第 0 个 · "1":扫到第 0 位,是数字 "1"。它不是小数点,处理起来最简单。
照抄 · "1":不是小数点,就把 "1" 原封不动接到结果串后面。现在结果串是 "1"。这一位也搞定,往后走。
读第 1 个 · "0":扫到第 1 位,是数字 "0"。它不是小数点,处理起来最简单。
照抄 · "0":不是小数点,就把 "0" 原封不动接到结果串后面。现在结果串是 "10"。这一位也搞定,往后走。
读第 2 个 · "0":扫到第 2 位,是数字 "0"。它不是小数点,处理起来最简单。
照抄 · "0":不是小数点,就把 "0" 原封不动接到结果串后面。现在结果串是 "100"。这一位也搞定,往后走。
读第 3 个 · ".":扫到第 3 位,是一个小数点 "."。按规矩,小数点不能直接抄,得换个写法。
替换 · "." → "[.]":是小数点,就往结果串后面写上 "[.]" 这三个字符,代替原来的一个点。写完结果串变成 "100[.]"。这一位处理好了,继续看下一个。
读第 4 个 · "2":扫到第 4 位,是数字 "2"。它不是小数点,处理起来最简单。
照抄 · "2":不是小数点,就把 "2" 原封不动接到结果串后面。现在结果串是 "100[.]2"。这一位也搞定,往后走。
读第 5 个 · ".":扫到第 5 位,是一个小数点 "."。按规矩,小数点不能直接抄,得换个写法。
替换 · "." → "[.]":是小数点,就往结果串后面写上 "[.]" 这三个字符,代替原来的一个点。写完结果串变成 "100[.]2[.]"。这一位处理好了,继续看下一个。
读第 6 个 · "3":扫到第 6 位,是数字 "3"。它不是小数点,处理起来最简单。
照抄 · "3":不是小数点,就把 "3" 原封不动接到结果串后面。现在结果串是 "100[.]2[.]3"。这一位也搞定,往后走。
读第 7 个 · "0":扫到第 7 位,是数字 "0"。它不是小数点,处理起来最简单。
照抄 · "0":不是小数点,就把 "0" 原封不动接到结果串后面。现在结果串是 "100[.]2[.]30"。这一位也搞定,往后走。
读第 8 个 · ".":扫到第 8 位,是一个小数点 "."。按规矩,小数点不能直接抄,得换个写法。
替换 · "." → "[.]":是小数点,就往结果串后面写上 "[.]" 这三个字符,代替原来的一个点。写完结果串变成 "100[.]2[.]30[.]"。这一位处理好了,继续看下一个。
读第 9 个 · "4":扫到第 9 位,是数字 "4"。它不是小数点,处理起来最简单。
照抄 · "4":不是小数点,就把 "4" 原封不动接到结果串后面。现在结果串是 "100[.]2[.]30[.]4"。这一位也搞定,往后走。
完成 · 答案 "100[.]2[.]30[.]4":10 个字符全扫完了。原串里的 3 个小数点都换成了 "[.]",数字一个没动,拼出来就是 "100[.]2[.]30[.]4"。可以对一下:原来的 "100.2.30.4" 里三个点的位置,现在各是一段 "[.]",答案 "100[.]2[.]30[.]4" 成立。
边界都一个套路:不管数字是几位、是不是 0,只认小数点换 "[.]",其余照抄。
面试重点:从右往左是为了下标不乱、时间空间都是 O(n)、工程里一行 replace 最省心。
参考代码
from __future__ import annotationsfrom typing import *from collections import *from functools import *from itertools import *from math import *from heapq import *from bisect import *class Solution: def defangIPaddr(self, address: str) -> str: return address.replace('.', '[.]')复杂度
- 时间:O(n),n 为地址长度。每个字符只看一遍,判断是不是小数点再决定抄一个还是写三个,整体是线性扫描
- 空间:O(n),要生成一个新字符串放结果。每个小数点会变成 3 个字符,所以新串长度最多约 3n,按峰值占用算是线性
易错点
面试追问把动画讲成自己的话
追问为什么 C++ 那版要从右往左替换?
追问时间和空间复杂度是多少?
追问实际工程里最简洁的写法是什么?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
按既定顺序创建目标数组
LeetCode 1389 · 简单 · 沿着 数组套路 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题