返回

动态规划方法:LeetCode 139 单词拆分

见解分享

动态规划:优化单词拆分算法

引言

在计算机科学领域,单词拆分问题 是一个经典的动态规划问题。给定一个字符串和一个字典,我们希望确定该字符串是否可以被拆分成字典中包含的单词的序列。

递归解法

最简单的单词拆分方法是递归 。对于字符串 s,我们可以尝试将其所有可能的前缀拆分成字典中的单词。如果前缀是有效的单词,则递归地尝试将剩余字符串拆分。

def word_break(s, word_dict):
  if not s:
    return True

  for i in range(1, len(s) + 1):
    prefix = s[:i]
    if prefix in word_dict and word_break(s[i:], word_dict):
      return True

  return False

动态规划解法

递归解法存在重叠子问题,导致效率低下。我们可以使用动态规划 优化它,通过存储子问题的解决方案来避免重复计算。

我们定义一个布尔型的表格 dp,其中 dp[i] 表示字符串 s[:i] 是否可以拆分。

dp = [False] * (len(s) + 1)
dp[0] = True  # 空字符串可以拆分

for i in range(1, len(s) + 1):
  for j in range(i):
    if dp[j] and s[j:i] in word_dict:
      dp[i] = True
      break

如果 dp[len(s)]True,则表明字符串 s 可以拆分。

时间和空间复杂度

  • 递归解法:最坏情况时间复杂度为 O(2^n),空间复杂度为 O(n),其中 n 是字符串的长度。
  • 动态规划解法:最坏情况时间复杂度为 O(n^2),空间复杂度为 O(n),其中 n 是字符串的长度。

代码示例

以下 Python 代码演示了动态规划单词拆分算法:

def word_break(s, word_dict):
  dp = [False] * (len(s) + 1)
  dp[0] = True

  for i in range(1, len(s) + 1):
    for j in range(i):
      if dp[j] and s[j:i] in word_dict:
        dp[i] = True
        break

  return dp[len(s)]

结论

动态规划是一种强大的技术,可用于优化递归算法,避免重复子问题计算。在单词拆分问题中,动态规划解法大大提高了效率,使我们能够快速确定一个字符串是否可以拆分成字典中单词的序列。

常见问题解答

  1. 动态规划和递归的区别是什么?
    • 递归是一种自顶向下的方法,不断分解问题直到找到解决方案。动态规划是一种自底向上的方法,逐步构建解决方案并存储中间结果。
  2. 动态规划算法的时间和空间复杂度如何?
    • 时间复杂度取决于问题的结构,对于单词拆分问题,它为 O(n^2)。空间复杂度取决于存储中间结果所需的内存量,对于单词拆分问题,它为 O(n)
  3. 动态规划算法如何处理重叠子问题?
    • 动态规划通过存储中间结果来处理重叠子问题。当需要解决子问题时,它首先检查它是否已被存储,如果是,则直接返回存储的结果。
  4. 动态规划算法适用于哪些其他问题?
    • 动态规划算法适用于许多问题,例如最长公共子序列、最长增长子序列和背包问题。
  5. 如何使用动态规划解决单词拆分问题?
    • 我们创建一个表格,其中每一行表示子字符串,每一列表示字典中的单词。我们填充表格,表示是否可以使用前面的单词拆分当前子字符串。如果表格的最后一列被填充,则字符串可以拆分。