返回

深入剖析LeetCode 392:判断子序列的精髓

前端

判别子序列:从零开始理解 LeetCode 经典题目

理解子序列

在计算机科学中,子序列 指的是从原始序列中删除任意数量的元素(也可以不删除)后得到的新序列。换句话说,子序列是原始序列的重新排列,元素的顺序与原始序列保持一致。

例如,字符串 "abc" 的子序列包括 "a", "b", "c", "ab", "ac", "bc", "abc"。

判断子序列

LeetCode 392 题要求我们判断一个字符串是否是另一个字符串的子序列。我们可以使用动态规划来有效地解决这个问题。

动态规划法

动态规划是一种解决复杂问题的方法,它将问题分解成更小的子问题,并依次解决这些子问题。对于判断子序列的问题,我们可以定义一个二维数组 dp,其中 dp[i][j] 表示字符串 s 的前 i 个字符是否是字符串 t 的前 j 个字符的子序列。

动态规划方程

dp 数组的计算方式如下:

  • 如果 s[i] == t[j], 那么 dp[i][j] = dp[i-1][j-1] + 1
  • 如果 s[i] != t[j], 那么 dp[i][j] = dp[i][j-1]

其中 dp[i-1][j-1] 表示 s 的前 i-1 个字符是否是 t 的前 j-1 个字符的子序列,dp[i][j-1] 表示 s 的前 i 个字符是否是 t 的前 j-1 个字符的子序列。

初始化

我们首先初始化 dp 数组的第一行和第一列:

  • dp[0][j] = 0,因为空字符串不是任何字符串的子序列
  • dp[i][0] = 1,因为空字符串是任何字符串的子序列

边界情况

当我们到达 s 的末尾时,dp[len(s)][j] 表示 s 是否是 t 的前 j 个字符的子序列。如果 dp[len(s)][len(t)] == len(s), 那么 st 的子序列。否则,s 不是 t 的子序列。

代码示例

def is_subsequence(s, t):
  """
  判断字符串 s 是否是字符串 t 的子序列。

  参数:
    s (str): 要判断的子序列字符串。
    t (str): 待匹配的原序列字符串。

  返回:
    bool: True 表示 s 是 t 的子序列,False 表示不是。
  """

  # 初始化 dp 数组
  dp = [[0] * (len(t) + 1) for _ in range(len(s) + 1)]

  # 初始化第一行和第一列
  for j in range(len(t) + 1):
    dp[0][j] = 0
  for i in range(len(s) + 1):
    dp[i][0] = 1

  # 计算 dp 数组
  for i in range(1, len(s) + 1):
    for j in range(1, len(t) + 1):
      if s[i-1] == t[j-1]:
        dp[i][j] = dp[i-1][j-1] + 1
      else:
        dp[i][j] = dp[i][j-1]

  # 判断 s 是否是 t 的子序列
  return dp[len(s)][len(t)] == len(s)

常见问题

1. 什么是动态规划?

动态规划是一种解决复杂问题的方法,它将问题分解成更小的子问题,并依次解决这些子问题。

2. 判断子序列的动态规划方程是什么?

如果 s[i] == t[j], 那么 dp[i][j] = dp[i-1][j-1] + 1
如果 s[i] != t[j], 那么 dp[i][j] = dp[i][j-1]

3. 如何判断字符串 s 是否是字符串 t 的子序列?

如果 dp[len(s)][len(t)] == len(s), 那么 st 的子序列。否则,s 不是 t 的子序列。

4. 判断子序列的时间复杂度是多少?

时间复杂度为 O(mn),其中 m 和 n 分别是字符串 st 的长度。

5. 判断子序列的空间复杂度是多少?

空间复杂度为 O(mn),因为我们使用了二维数组 dp 来存储中间结果。