leetcode
leetcode 51 ~ 100
复原 IP 地址

复原 IP 地址

难度:

标签:

题目描述

有效 IP 地址 正好由四个整数(每个整数位于 0255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。

  • 例如:"0.1.2.201" "192.168.1.1"有效 IP 地址,但是 "0.011.255.245""192.168.1.312""[email protected]"无效 IP 地址。

给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 '.' 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。

 

示例 1:

输入:s = "25525511135"
输出:["255.255.11.135","255.255.111.35"]

示例 2:

输入:s = "0000"
输出:["0.0.0.0"]

示例 3:

输入:s = "101023"
输出:["1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"]

 

提示:

  • 1 <= s.length <= 20
  • s 仅由数字组成

代码结果

运行时间: 64 ms, 内存: 15 MB


/*
 * 题目思路:
 * 1. 使用流式API,将字符串划分成可能的IP地址片段。
 * 2. 过滤出符合IP地址规范的片段组合。
 * 3. 将结果收集为列表。
 */
 
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
 
public class RestoreIPAddressesStream {
    public List<String> restoreIpAddresses(String s) {
        return IntStream.range(1, s.length()).boxed()
            .flatMap(i -> IntStream.range(i + 1, s.length()).boxed()
            .flatMap(j -> IntStream.range(j + 1, s.length()).boxed()
            .map(k -> new int[]{i, j, k})))
            .map(indices -> new String[]{s.substring(0, indices[0]), s.substring(indices[0], indices[1]), s.substring(indices[1], indices[2]), s.substring(indices[2])})
            .filter(parts -> isValid(parts))
            .map(parts -> String.join(".", parts))
            .collect(Collectors.toList());
    }
 
    private boolean isValid(String[] parts) {
        return parts.length == 4 && 
               IntStream.range(0, 4).allMatch(i -> isValidSegment(parts[i]));
    }
 
    private boolean isValidSegment(String segment) {
        if (segment.length() > 1 && segment.startsWith("0")) return false;
        int value = Integer.parseInt(segment);
        return value >= 0 && value <= 255;
    }
}
 

解释

方法:

这道题使用回溯算法来解决。基本思路是:从字符串的左侧开始,依次选取1到3个字符作为IP地址的一段,然后递归处理剩余部分。在递归过程中,如果已经选取了4段并且字符串恰好用完,则找到一个有效的IP地址。递归回溯的过程中注意两点:1. 每一段必须在0~255的范围内;2. 如果一段包含多位数字,则第一位不能为0。

时间复杂度:

O(1)

空间复杂度:

O(1)

代码细节讲解

🦆
在递归回溯过程中,为什么选择分割长度从1到3个字符,这与IP地址的哪些特性相关?
IP地址中每个数字段的取值范围是0到255,这意味着每个数字段的字符长度最多是3(例如,255)。最短则为1个字符(例如,0)。因此,在递归过程中考虑1到3个字符长度的分割是为了确保每个段都可能是一个有效的IP地址段。
🦆
算法中提到,如果一段有前导零则跳过,这样做的原因是什么?是否所有包含前导零的字符串段都不可能成为有效的IP地址段?
在IP地址中,一个数字段如果以0开始并且长度超过1,通常被认为是无效的,因为IP地址中的每个数字段应该是一个没有前导零的十进制数。唯一的例外是数字0本身,它可以是一个单独的段。因此,除了单个'0'之外,任何带有前导零的段都被认为是无效的。
🦆
在判断一个段是否大于255时,是否考虑了某些边界情况,例如字符串形式的数字超过了整型的常规处理范围?
在这个特定的算法中,由于每个段的最大长度为3,因此字符串形式的数字最大就是999,远低于整型变量(例如,32位整型)的处理极限。因此,在这个场景下,没有必要担心整型溢出或处理超过常规范围的数字。
🦆
递归函数中,`begin` 变量的作用具体是什么?它如何帮助处理字符串的剩余部分?
`begin` 变量在递归函数中标识当前处理的起始位置。每次递归调用时,通过增加 `begin` 的值来移动到字符串的下一个部分,这样可以确保每次递归处理的都是字符串的剩余部分。`begin` 变量帮助算法保持追踪当前分析的字符串位置,避免重复处理已经分析过的部分。

相关问题

IP 到 CIDR

IP 到 CIDR