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

使A成为B子序列的最小替换DP解法直觉思路解析

替换B中最少元素使A成为其子序列的DP解法解析

问题描述

给定长度为n的数组A和长度为m的数组B(满足n≤m),需求解替换B中最少元素的数量,使得A成为B的子序列(子序列无需连续索引)。

示例

  • 当A=[1,5,2,3]、B=[1,4,2,5,6,2,5]时,答案为1;
  • 当A=[1,5,2,3]、B=[5,8,8,2,8,8,3]时,答案为2。

要求解法时间复杂度为O(n*m),官方Java实现代码如下:

public static int editToSubsequence(int n, int m, int[] A, int[] B) {
    // IDEA: Dynamic programming DP[i][j] = minimum number of edits such that
    // indices 0...i-1 of A are a subsequence of indices 0...j-1 of B.
    // We set DP[i][j] = n + 1 for j < i, because that case is impossible.
    // Otherwise, the main recursion is:
    //   - if A[i - 1] = B[j - 1], we can set DP[i][j] = DP[i - 1][j - 1]
    //   - else, we can set DP[i][j] = DP[i - 1][j - 1] if we edit B[j],
    //    or DP[i][j] = DP[i][j - 1] if we do not use B[j]
    
    int[][] DP = new int[n + 1][m + 1];
    for (int i = 1; i <= n; i++) {
      for (int j = 0; j < i; j++) {
        DP[i][j] = n + 1;
      }
    }
    
    for (int i = 1; i <= n; i++) {
      for (int j = i; j <= m; j++) {
        if (A[i - 1] == B[j - 1]) {
          DP[i][j] = DP[i - 1][j - 1];
        } else {
          DP[i][j] = Math.min(DP[i][j - 1], DP[i - 1][j - 1] + 1);
        }
      }
    }
    
    return DP[n][m];
  }

疑问

本人熟悉最长公共子序列(Longest Common Subsequence, LCS)和最小编辑距离(Minimal Edit Distance)的动态规划解法,但无法理解该方法的直觉来源,希望得到相关解释。


DP解法直觉解释

1. DP状态的核心定义

DP[i][j]表示:让A的前i个元素(即A[0]到A[i-1])成为B的前j个元素(即B[0]到B[j-1])的子序列,所需的最少替换次数。

这个状态延续了你熟悉的“前缀匹配”思路,但目标更聚焦:只允许修改B的元素,最终要让A能按顺序匹配B中的元素(子序列要求)。

2. 初始条件的合理性

代码中对j < i的情况设置DP[i][j] = n + 1,原因很直接:如果B的前j个元素长度比A的前i个元素短(j<i),根本不可能让A的前i个元素成为B的前j个的子序列(子序列长度不能超过原序列)。用n+1标记是因为最多只需要替换n次(把B的前n个元素全换成A的元素),这个值大于所有可能的有效替换次数,用来表示“不可能”的情况。

3. 状态转移的逻辑推导

分两种核心情况讨论,对应代码里的分支:

情况1:A[i-1] == B[j-1]

此时A的第i个元素和B的第j个元素完全匹配,不需要对B[j-1]做任何操作。直接沿用“让A的前i-1个元素成为B的前j-1个元素的子序列”的最少替换次数即可,也就是DP[i][j] = DP[i-1][j-1]。

这和LCS中遇到匹配字符时继承左上角状态的思路一致——找到了一个可以直接复用的匹配点,无需额外操作。

情况2:A[i-1] != B[j-1]

此时有两种可选策略,我们取替换次数更少的那个:

  • 策略1:忽略B[j-1]:既然这个元素和A的当前元素不匹配,我们可以直接跳过它,问题转化为“让A的前i个元素成为B的前j-1个元素的子序列”的最少替换次数,即DP[i][j-1]。这和LCS中遇到不匹配字符时取左侧状态的思路类似。
  • 策略2:替换B[j-1]:把B[j-1]替换成A[i-1],这样这个位置就和A的当前元素匹配了。此时需要在“让A的前i-1个元素成为B的前j-1个元素的子序列”的基础上,再加1次替换操作,即DP[i-1][j-1] + 1。这和最小编辑距离中“替换”操作的逻辑对应,但这里只能修改B的元素,所以是围绕匹配A的元素来做替换。

4. 和LCS、编辑距离的关联

  • 与LCS的关联:这个问题也可以通过LCS推导:找到A和B的最长公共子序列长度L,那么最少替换次数等价于n - L(因为公共子序列部分无需替换,只需替换A中剩余的n-L个对应位置的B元素)。而DP方法直接聚焦在替换次数上,比LCS的转化更直接,避免了额外的推导步骤。
  • 与编辑距离的关联:编辑距离允许插入、删除、替换三种操作,而这里只允许替换B的元素,且要求A成为B的子序列(即B可以保留多余元素,只需要通过替换部分元素让A能按顺序匹配),所以状态转移的选项更少,逻辑更聚焦。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 13:27:33