📜  门| GATE-CS-2003 |第 58 题(1)

📅  最后修改于: 2023-12-03 14:58:25.274000             🧑  作者: Mango

题目描述

给定两个字符串 S 和 T,求 S 的子序列中有多少个 T。

例如, S = "rabbbit",T = "rabbit",S 的子序列中有 3 个 T。

思路

本题是一道经典的动态规划问题。设 $dp[i][j]$ 表示在字符串 $S[0:i]$ 中,$T[0:j]$ 出现的子序列的个数。

边界条件为 $dp[i][0] = 1$。即,无论 $S$ 如何,当 $T$ 是空字符串时,$dp[i][0]$ 均为 1。而 $dp[0][j]$ 均为 0,因为若 $S$ 是空字符串,其不可能有 $T$ 的子序列。

状态转移方程为:

如果 $S[i-1] \neq T[j-1]$,则 $dp[i][j] = dp[i-1][j]$。即,若 $S$ 的第 $i$ 个字符不等于 $T$ 的第 $j$ 个字符,则 $S[0:i]$ 中有 $T[0:j]$ 出现的子序列个数与 $S[0:i-1]$ 中有 $T[0:j]$ 出现的子序列个数相同。

如果 $S[i-1] = T[j-1]$, 则 $dp[i][j] = dp[i-1][j] + dp[i-1][j-1]$,意为 $S[0:i]$ 中既可以不包括第 $i$ 个字符(于是有 $dp[i-1][j]$ 种情况),也可以包括第 $i$ 个字符(此时,$S[0:i-1]$ 中有 $T[0:j-1]$ 出现的子序列 $dp[i-1][j-1]$ 种情况,对它们分别加上第 $i$ 个字符即可得到 $S[0:i]$ 中包括第 $i$ 个字符的情况)。

最终可以返回 $dp[n][m]$,其中 $n$ 是 $S$ 的长度,$m$ 是 $T$ 的长度。

以下是完整的示例代码:

def num_distinct(s: str, t: str) -> int:
    n, m = len(s), len(t)
    dp = [[0] * (m+1) for _ in range(n+1)]
    for i in range(n+1):
        dp[i][0] = 1
    for i in range(1, n+1):
        for j in range(1, m+1):
            if s[i-1] != t[j-1]:
                dp[i][j] = dp[i-1][j]
            else:
                dp[i][j] = dp[i-1][j] + dp[i-1][j-1]
    return dp[n][m]

复杂度分析

本算法的时间复杂度为 $O(nm)$,其中 $n$ 是 $S$ 的长度, $m$ 是 $T$ 的长度。

空间复杂度为 $O(nm)$,主要是存储状态数组 $dp$。

参考链接

  • https://leetcode-cn.com/problems/distinct-subsequences/solution/dong-tai-gui-hua-by-powcai-5/