算法题:不同的子序列
举个栗子
输入: s = "rabbbit", t = "rabbit"
输出:3
为什么是3呢?因为rabbbit
有 3 种方式删出rabbit
:第1个 b
和后面的it
;第2个 b
和后面的it
;第3个 b
和后面的it
。
动态规划(Dynamic Programming)救场
dp
,其中:dp[i][j]
表示用 s[0:i] 的前缀字符串匹配 t[0:j] 的前缀字符串的方案数。
转移方程
如果 **s[i-1] == t[j-1]**:
可以选择匹配:方案数是 dp[i-1][j-1]
;也可以不匹配:方案数是 dp[i-1][j]
。总数: dp[i][j] = dp[i-1][j-1] + dp[i-1][j]
那就只能跳过当前的 **s[i-1]**,即 dp[i][j] = dp[i-1][j]
。
边界条件 🚀
dp[i][0] = 1
,因为 t 是空字符串,空字符串是任何字符串的子序列;dp[0][j] = 0
(当 j > 0),因为空的 s 无法匹配非空的 t。
上代码
def numDistinct(s: str, t: str) -> int:
m, n = len(s), len(t)
# 创建 DP 数组,初始化为 0
dp = [[0] * (n + 1) for _ in range(m + 1)]
# 初始化边界条件
for i in range(m + 1):
dp[i][0] = 1 # 空的 t 是任何 s 的子序列
# 填 DP 表
for i in range(1, m + 1):
for j in range(1, n + 1):
if s[i - 1] == t[j - 1]:
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]
else:
dp[i][j] = dp[i - 1][j]
return dp[m][n]
# 测试
print(numDistinct("rabbbit", "rabbit")) # 输出:3
核心思路
优化:空间压缩 🛠️
def numDistinct(s: str, t: str) -> int:
m, n = len(s), len(t)
prev = [0] * (n + 1)
curr = [0] * (n + 1)
prev[0] = 1 # 空的 t 是任何 s 的子序列
for i in range(1, m + 1):
curr[0] = 1
for j in range(1, n + 1):
if s[i - 1] == t[j - 1]:
curr[j] = prev[j - 1] + prev[j]
else:
curr[j] = prev[j]
prev, curr = curr, [0] * (n + 1)
return prev[n]
print(numDistinct("rabbbit", "rabbit")) # 输出:3
对编程、职场感兴趣的同学,大家可以联系我微信:golang404,拉你进入“程序员交流群”。
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》。