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

递归+记忆化DP数组填充顺序问题:如何反转填充顺序?

修改递归逻辑以匹配迭代DP的填充顺序

问题本质

原递归+记忆化的实现是自顶向下的:从整个字符串的两端(i=0, j=n-1)开始,递归拆分出更小的子问题求解,因此DP数组的填充顺序是先填充大区间(长串),再填充小区间(短串)。而从索引0开始的嵌套循环是自底向上的:先填充所有长度为1的子串,再依次填充长度2、3...直到整个字符串,填充顺序是小区间先于大区间。

要让递归的DP填充顺序和迭代一致,核心是主动按子串长度从小到大的顺序触发递归求解,确保计算当前区间时,所有依赖的子问题已经被处理完毕。

具体修改方案

方案1:按子串长度递归遍历(推荐)

直接模拟迭代的逻辑,用递归按子串长度从小到大处理,先完成所有小区间的DP填充,再处理大区间:

def min_insertions(s):
    n = len(s)
    # 初始化DP数组,-1表示未填充
    dp = [[-1] * n for _ in range(n)]
    
    # 先处理所有长度为1的子串(i==j),无需插入
    for i in range(n):
        dp[i][i] = 0
    
    # 递归函数:处理所有长度为l的子串
    def dfs(l):
        # 递归终止:子串长度超过字符串长度
        if l > n:
            return
        # 遍历所有长度为l的子串起始索引i
        for i in range(n - l + 1):
            j = i + l - 1
            if s[i] == s[j]:
                # 两端字符相等,直接取内层子串的结果(长度l-2,已处理)
                dp[i][j] = dp[i+1][j-1]
            else:
                # 取左右子串的最小值+1(长度l-1,已处理)
                dp[i][j] = min(dp[i+1][j], dp[i][j-1]) + 1
        # 递归处理更长一级的子串
        dfs(l + 1)
    
    # 从长度为2的子串开始触发递归
    dfs(2)
    return dp[0][n-1]

示例验证

  • 对于"madam"(长度5):递归会先处理长度1的子串→长度2→长度3→长度4→长度5,DP填充顺序和迭代完全一致。
  • 对于"leetcode"(长度8):递归依次处理长度1到8的子串,每一步的子问题都已提前填充,最终DP数组的填充顺序和嵌套循环完全匹配。

方案2:原递归中优先求解子问题

如果不想完全重构递归逻辑,可以在原函数中先递归求解所有依赖的子问题,再计算当前区间的结果,强制让小区间先被填充:

def min_insertions(s):
    n = len(s)
    dp = [[-1] * n for _ in range(n)]
    
    def dfs(i, j):
        if i >= j:
            return 0
        if dp[i][j] != -1:
            return dp[i][j]
        
        # 先递归求解所有依赖的子问题(强制先填充小区间)
        dfs(i+1, j)    # 长度为j-i的子串
        dfs(i, j-1)    # 长度为j-i的子串
        dfs(i+1, j-1)  # 长度为j-i-2的子串
        
        # 现在计算当前区间的结果
        if s[i] == s[j]:
            dp[i][j] = dp[i+1][j-1]
        else:
            dp[i][j] = min(dp[i+1][j], dp[i][j-1]) + 1
        return dp[i][j]
    
    dfs(0, n-1)
    return dp[0][n-1]

说明

这种方式会提前触发所有子问题的求解,因此DP数组会先填充所有小区间,再逐步填充大区间,最终和迭代顺序一致。但注意会存在重复递归调用(比如dfs(1,3)可能被多个上层问题触发),效率略低于方案1。

核心总结

  • 要匹配迭代的填充顺序,必须先处理所有更小的子问题,再处理当前问题。
  • 方案1直接模拟迭代的长度遍历逻辑,用递归替代循环,填充顺序完全一致且效率较高。
  • 方案2通过提前递归求解子问题,强制调整填充顺序,适合不想大幅修改原有递归结构的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 13:13:17