复原 IP 地址

标签: 字符串 回溯

难度: Medium

给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能从 s 获得的 有效 IP 地址 。你可以按任何顺序返回答案。

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

例如:"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址,但是 "0.011.255.245"、"192.168.1.312" 和 "192.168@1.1" 是 无效 IP 地址。

示例 1:

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

示例 2:

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

示例 3:

输入:s = "1111"
输出:["1.1.1.1"]

示例 4:

输入:s = "010010"
输出:["0.10.0.10","0.100.1.0"]

示例 5:

输入:s = "10203040"
输出:["10.20.30.40","102.0.30.40","10.203.0.40"]

提示:

  • 0 <= s.length <= 3000
  • s 仅由数字组成

注意:本题与主站 93 题相同:https://leetcode-cn.com/problems/restore-ip-addresses/ 

Submission

运行时间: 22 ms

内存: 16.0 MB

class Solution:
    def restoreIpAddresses(self, s: str) -> List[str]:
        def search(s, last):
            if len(s) == 0:
                if len(last.split(".")) == 5:
                    ans.append(last)
                return
            if len(last.split(".")) > 4:
                return 
            if s[0] == '0':
                search(s[1:], last + '.' + s[0])
            else:
                for i in range(min(3, len(s))):
                    if int(s[:i + 1]) > 255:
                        continue
                    search(s[i + 1:], last + '.' + s[:i + 1])
        ans = []
        search(s, '')
        # print(ans)
        return [a.lstrip('.') for a in ans]
            

Explain

本题解使用了递归回溯的方法来构建所有可能的有效IP地址。从字符串的开始位置出发,通过递归地尝试每一个可能的分段(最长为3个字符),并且每段数字必须在0到255之间。特别地,单个'0'可以作为一段,但是'0'不能作为其他数字的前缀。在每一步递归中,如果当前的部分字符串符合IP地址的一个段的要求,就将其加到当前正在构建的IP地址中。当字符串被完全使用并且构建的IP地址由四段组成时,这个地址就被认为是一个有效的IP地址。通过这种方式,递归函数遍历所有可能的分段方式,直到找到所有的有效IP地址。

时间复杂度: O(1)

空间复杂度: O(n)

class Solution:
    def restoreIpAddresses(self, s: str) -> List[str]:
        def search(s, last):
            # Base case: if the string is empty
            if len(s) == 0:
                # Check if the constructed IP has exactly four parts
                if len(last.split('.')) == 5:
                    ans.append(last)
                return
            # If already have more than 4 parts, return
            if len(last.split('.')) > 4:
                return
            # Special handling for segments starting with '0'
            if s[0] == '0':
                search(s[1:], last + '.' + s[0])
            else:
                # Try all possible lengths (1 to 3) for the current segment
                for i in range(min(3, len(s))):
                    # Check if the segment is a valid integer less than or equal to 255
                    if int(s[:i + 1]) > 255:
                        continue
                    search(s[i + 1:], last + '.' + s[:i + 1])
        ans = []
        search(s, '')
        # Strip the leading '.' from the constructed IP addresses before returning
        return [a.lstrip('.') for a in ans]

Explore

在递归回溯算法中使用字符串拼接来构建IP地址片段主要是因为字符串拼接简单直观且易于操作。使用字符串可以直接通过添加'.'来分隔IP地址中的各个部分,这样的操作直接反映了IP地址的结构。此外,字符串拼接使得在递归的每一层中传递和修改地址片段变得更加方便。如果使用数组或其他数据结构,虽然可以提供更灵活的数据操作,但在每次递归调用中需要更多的操作来管理和转换数据格式,比如在递归结束后还需要将数组转换为字符串格式以符合IP地址的标准显示格式。因此,考虑到实现的简洁性和直观性,字符串拼接是一种有效的选择。

在IP地址中,'0'可以单独作为一个段,但是不能作为其他数字的前缀。这是因为在IP地址的表示中,前导零是不允许的,例如'01'或'001'是不合法的。因此,如果一个段以'0'开头,它必须单独作为一个段,后面的数字不能包含在这个段内,否则会导致误解或不被接受的IP格式。这种处理方式符合IP地址的标准规定,确保了生成的IP地址的有效性。

是的,这种设计能有效地减少不必要的递归调用。IP地址由四个数字段组成,每个字段由1到3个数字构成。如果在递归过程中已经构建了超过四个段,那么继续递归不仅没有意义,而且会导致生成无效的IP地址。通过设置递归退出条件为段数超过四个,可以及时终止那些不可能生成有效IP地址的路径,从而提高算法的效率和性能。这种策略是减少计算量和避免无效计算的有效方法。