返回

复原 IP 地址:巧用动态规划,简洁明了!

后端

问题概述

给定一个由数字组成的字符串,可以将其划分为四个部分,每部分都是一个数字(0 到 255 之间),且不能含有前导 0。组合这些部分可以得到一个 IP 地址。例如,"19216811"可以被划分为"192.168.1.1"或"192.168.11.1"。

动态规划解法

动态规划是一种自底向上的算法,可以将复杂问题分解为一系列子问题,然后逐步解决这些子问题,最终求得整体问题的解决方案。

在复原 IP 地址问题中,我们可以定义状态dp[i]为从字符串第i个字符开始到字符串末尾的所有可能的 IP 地址的集合。然后,我们可以通过以下递推关系来计算dp[i]

  • dp[i] = {}(空集),如果i大于等于字符串长度
  • dp[i] = {a.b.c.d},如果i等于字符串长度,其中a, b, c, d都是有效的 IP 地址段
  • dp[i] = {a.b.c.d},如果i小于字符串长度,且字符串第i个字符到i+3个字符组成的数字是一个有效的 IP 地址段,并且dp[i+4]非空

以上递推关系的含义是:如果字符串第i个字符到i+3个字符组成的数字是一个有效的 IP 地址段,并且从第i+4个字符开始到字符串末尾的所有可能的 IP 地址的集合非空,那么从第i个字符开始到字符串末尾的所有可能的 IP 地址的集合就是由"a.b.c.d"组成的集合,其中"a", "b", "c", "d"都是有效的 IP 地址段。

代码示例

def restore_ip_addresses(s):
    """
    :type s: str
    :rtype: List[str]
    """
    # 定义状态dp[i]为从字符串第i个字符开始到字符串末尾的所有可能的IP地址的集合
    dp = [set() for _ in range(len(s) + 1)]
    # 初始化dp[len(s)]为{}(空集)
    dp[len(s)] = {""}

    # 从后往前计算dp[i]
    for i in range(len(s) - 1, -1, -1):
        # 如果字符串第i个字符到i+3个字符组成的数字是一个有效的IP地址段
        if 0 <= int(s[i:i + 1]) <= 255:
            # 将dp[i+4]中的所有IP地址与s[i:i+1]连接起来,得到dp[i]中的所有IP地址
            for ip_address in dp[i + 1]:
                dp[i].add(s[i:i + 1] + "." + ip_address)
        # 如果字符串第i个字符到i+2个字符组成的数字是一个有效的IP地址段
        if 0 <= int(s[i:i + 2]) <= 255:
            # 将dp[i+3]中的所有IP地址与s[i:i+2]连接起来,得到dp[i]中的所有IP地址
            for ip_address in dp[i + 2]:
                dp[i].add(s[i:i + 2] + "." + ip_address)
        # 如果字符串第i个字符到i+3个字符组成的数字是一个有效的IP地址段
        if 0 <= int(s[i:i + 3]) <= 255:
            # 将dp[i+4]中的所有IP地址与s[i:i+3]连接起来,得到dp[i]中的所有IP地址
            for ip_address in dp[i + 3]:
                dp[i].add(s[i:i + 3] + "." + ip_address)

    # 返回dp[0]中的所有IP地址
    return list(dp[0])

结语

复原 IP 地址问题是一个经典的算法题,可以考察考生的动态规划能力和对字符串的处理能力。本文介绍的动态规划解法简洁明了,易于理解和实现。希望这篇文章能帮助你更好地理解复原 IP 地址问题,并掌握动态规划的思想和方法。