You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

理解不同子序列DP基准情况:两种DP写法边界条件差异解析

不同子序列问题:两种DP解法的边界条件差异

我在解决LeetCode的「不同子序列问题」:给定两个字符串s和t,返回s的子序列中等于t的不同子序列数目。例如s="rabbbit"、t="rabbit"时答案为3。

先实现了可行的自底向上DP解法:

# bottom up - working
def numDistinct(self, s: str, t: str) -> int:
    
    @cache # (i,j): number of distinct subsequences in s[i:] that equal t[j:]
    def aux(i, j):            
        if j == len(t): return 1 
        if i == len(s): return 0 
        
        if s[i] == t[j]:
            return aux(i+1, j+1) + aux(i+1, j)
        else:
            return aux(i+1, j)
        
    return aux(0,0)

该解法通过了测试。随后尝试自顶向下DP解法,初始版本无法得到正确结果(上述示例输出为0而非3)。通过手动推演,修改边界条件后解法可行:

# top down - working
def numDistinct(self, s: str, t: str) -> int:

    @cache        
    def aux(i, j):
        
        if i == -1: return 1 if j == -1 else 0 # if s exhausts before t, return 0, else 1
        if j == -1: return 1
        
        if s[i] == t[j]:
            return aux(i-1,j-1) + aux(i-1, j)
        else:
            return aux(i-1, j)
        
    return aux(len(s)-1, len(t)-1)

疑问解析

核心原因是边界判断的优先级和递归的逻辑路径差异:

在自底向上的递归逻辑中,我们先判断if j == len(t): return 1——这意味着只要t已经完全匹配完成(j走到了末尾),不管s有没有剩余(i是否到末尾),都直接返回1,因为空的t本身就是任何s的子序列。

当执行到if i == len(s): return 0的时候,说明前面的j == len(t)判断没有触发,也就是j还没走到t的末尾。此时s已经耗尽,不可能再匹配出剩下的t[j:],所以直接返回0就足够,不需要额外判断j的状态。

反观自顶向下的递归,我们从字符串末尾往开头遍历,边界是i=-1(s遍历完)和j=-1(t遍历完):

  • 如果j=-1,说明t已经匹配完,不管s有没有剩余,都返回1;
  • 如果i=-1但j≠-1,说明s耗完了但t还没匹配完,返回0;
    这两种情况必须分开判断,因为递归路径里可能先走到i=-1的状态,此时需要明确j的状态才能返回正确结果。

简单来说,自底向上的递归里,j == len(t)的判断优先级更高,已经覆盖了所有“t匹配完成”的场景,剩下的i到末尾的情况必然是t未匹配完成,所以直接返回0即可。

内容的提问来源于stack exchange,提问作者Maha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 04:20:59