文章目录
题目
标题和出处
标题:复原 IP 地址
出处:93. 复原 IP 地址
难度
5 级
题目描述
要求
有效 IP 地址 由恰好四个整数和分隔整数的点组成。每个整数在范围 0 \texttt{0} 0 到 255 \texttt{255} 255 中,且不能含有前导零。
- 例如: "0.1.2.201" \texttt{"0.1.2.201"} "0.1.2.201" 和 "192.168.1.1" \texttt{"192.168.1.1"} "192.168.1.1" 是有效 IP 地址,但是 "0.011.255.245" \texttt{"0.011.255.245"} "0.011.255.245"、 "192.168.1.312" \texttt{"192.168.1.312"} "192.168.1.312" 和 "192.168@1.1" \texttt{"192.168@1.1"} "192.168@1.1" 是无效 IP 地址。
给定一个只包含数字的字符串 s \texttt{s} s,返回通过在 s \texttt{s} s 中插入点可以得到的所有可能的有效 IP 地址。不能 重新排序或删除 s \texttt{s} s 中的任何数字。可以按任意顺序返回答案。
示例
示例 1:
输入: s = "25525511135" \texttt{s = "25525511135"} s = "25525511135"
输出: "255.255.11.135","255.255.111.35" \texttt{"255.255.11.135","255.255.111.35"} "255.255.11.135","255.255.111.35"
示例 2:
输入: s = "0000" \texttt{s = "0000"} s = "0000"
输出: "0.0.0.0" \texttt{"0.0.0.0"} "0.0.0.0"
示例 3:
输入: s = "101023" \texttt{s = "101023"} s = "101023"
输出: "1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3" \texttt{"1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"} "1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"
数据范围
- 1 ≤ s.length ≤ 20 \texttt{1} \le \texttt{s.length} \le \texttt{20} 1≤s.length≤20
- s \texttt{s} s 仅由数字组成
解法
思路和算法
由于有效 IP 地址由 4 4 4 个整数组成,因此需要在字符串 s s s 中插入 3 3 3 个点得到有效的 IP 地址。可以使用回溯得到所有可能的有效 IP 地址。
回溯过程中需要记录当前的起始下标 start \textit{start} start 和片段数 segmentCount \textit{segmentCount} segmentCount,使用长度为 4 4 4 的数组记录每个片段对应的整数,初始时 start = 0 \textit{start} = 0 start=0, segmentCount = 0 \textit{segmentCount} = 0 segmentCount=0。对于每个片段,只有当前片段不含前导零且对应的整数在范围 0 , 255 0, 255 0,255 中,才可能是有效 IP 地址,此时继续遍历后面的片段。具体做法如下。
-
如果 segmentCount = 4 \textit{segmentCount} = 4 segmentCount=4,则只有当字符串 s s s 遍历结束时才能得到有效的 IP 地址。当 start = n \textit{start} = n start=n 时,字符串 s s s 遍历结束,根据数组中的整数生成 IP 地址,添加到答案中。
-
如果 segmentCount < 4 \textit{segmentCount} < 4 segmentCount<4,则当前片段对应的片段编号为 segmentCount \textit{segmentCount} segmentCount。用 end \textit{end} end 表示当前片段的结束下标,用 s start : end s\\textit{start} : \\textit{end} sstart:end 表示字符串 s s s 的下标范围 start , end \\textit{start}, \\textit{end} start,end 的子数组,将 end \textit{end} end 从 start \textit{start} start 到 n − 1 n - 1 n−1 依次遍历,执行如下操作。
-
如果 s start : end s\\textit{start} : \\textit{end} sstart:end 不含前导零且对应的整数在范围 0 , 255 0, 255 0,255 中,则当前片段可能是有效 IP 地址的一个片段,将当前片段对应的整数填入数组的下标 segmentCount \textit{segmentCount} segmentCount 处,继续对下一个片段回溯,下一个片段的起始下标是 end + 1 \textit{end} + 1 end+1,下一个片段的片段数是 segmentCount + 1 \textit{segmentCount} + 1 segmentCount+1。
-
如果 s start : end s\\textit{start} : \\textit{end} sstart:end 含前导零或对应的整数不在范围 0 , 255 0, 255 0,255 中,则当前片段不可能是有效 IP 地址的一个片段,如果继续将 end \textit{end} end 向右移动则当前片段一定不符合有效 IP 地址的规则,因此结束对当前 start \textit{start} start 的回溯。
-
回溯结束时,即可得到所有可能的有效 IP 地址。
实现方面,由于有效 IP 地址的每个片段的长度都在范围 1 , 3 1, 3 1,3 中,因此有效 IP 地址的数字位数(不包括点)应该在范围 4 , 12 4, 12 4,12 中。如果 s s s 的长度小于 4 4 4 或大于 12 12 12,则一定不存在有效 IP 地址,返回空列表。只有当 s s s 的长度在范围 4 , 12 4, 12 4,12 中时才需要使用回溯得到所有可能的有效 IP 地址。
代码
java
class Solution {
static final int SEGMENTS = 4;
List<String> ipAddresses = new ArrayList<String>();
int[] ipArr = new int[SEGMENTS];
int n;
String s;
public List<String> restoreIpAddresses(String s) {
this.n = s.length();
this.s = s;
if (n < SEGMENTS || n > 3 * SEGMENTS) {
return ipAddresses;
}
backtrack(0, 0);
return ipAddresses;
}
public void backtrack(int start, int segmentCount) {
if (segmentCount == SEGMENTS) {
if (start == n) {
ipAddresses.add(convert(ipArr));
}
} else {
boolean flag = true;
for (int end = start; end < n && flag; end++) {
if (valid(start, end)) {
ipArr[segmentCount] = Integer.parseInt(s.substring(start, end + 1));
backtrack(end + 1, segmentCount + 1);
} else {
flag = false;
}
}
}
}
public boolean valid(int start, int end) {
int length = end - start + 1;
if (length < 1 || length > 3) {
return false;
}
if (length > 1 && s.charAt(start) == '0') {
return false;
}
return Integer.parseInt(s.substring(start, end + 1)) <= 255;
}
public String convert(int[] ipArr) {
StringBuffer sb = new StringBuffer();
sb.append(ipArr[0]);
for (int i = 1; i < SEGMENTS; i++) {
sb.append('.');
sb.append(ipArr[i]);
}
return sb.toString();
}
}
复杂度分析
-
时间复杂度: O ( n 4 ) O(n^4) O(n4),其中 n n n 是字符串 s s s 的长度。由于有效 IP 地址有 4 4 4 个片段,因此需要插入 3 3 3 个点,插入点的方案数是 O ( n 3 ) O(n^3) O(n3),每种方案需要 O ( n ) O(n) O(n) 的时间得到 IP 地址和将有效 IP 地址添加到答案中,因此时间复杂度是 O ( n 4 ) O(n^4) O(n4)。
-
空间复杂度: O ( C ) O(C) O(C),其中 C C C 是有效 IP 地址的片段数, C = 4 C = 4 C=4。需要创建长度为 C C C 的数组记录每个片段对应的整数,递归调用栈的层数是 O ( C ) O(C) O(C)。注意返回值不计入空间复杂度。