最长公共子序列的变种:带限制的最长公共子序列
字数 1451 2025-10-28 08:36:45

最长公共子序列的变种:带限制的最长公共子序列

题目描述
给定三个字符串 s1s2s3,求 s1s2 的最长公共子序列(LCS),且要求这个公共子序列中必须包含 s3 作为其子序列(即 s3 是 LCS 的一个连续或非连续子序列)。如果不存在满足条件的公共子序列,返回 0。

例如:

  • 输入:s1 = "abcde", s2 = "ace", s3 = "ae"
    输出:3(LCS 为 "ace",包含 "ae"
  • 输入:s1 = "abcde", s2 = "ace", s3 = "af"
    输出:0(LCS "ace" 不包含 "af"

解题思路
本题是 LCS 的扩展,需在常规 LCS 的动态规划过程中增加对 s3 的约束。我们可以分两步处理:

  1. 先求 s1s2 的 LCS(经典 DP)。
  2. 检查 LCS 是否包含 s3,但直接提取 LCS 再判断会超时(LCS 可能指数级数量),因此需在 DP 过程中同时追踪与 s3 的匹配情况。

改进方法
定义三维 DP 数组 dp[i][j][k]

  • 表示 s1 的前 i 个字符、s2 的前 j 个字符的公共子序列中,匹配到 s3 的前 k 个字符时的最长公共子序列长度
  • k = len(s3),说明已完全包含 s3

状态转移

  1. s1[i-1] == s2[j-1]
    • 字符可加入公共子序列,检查它是否与 s3[k] 匹配:
      • 若匹配,则 k 可增加 1:dp[i][j][k] = max(dp[i][j][k], dp[i-1][j-1][k-1] + 1)
      • 无论是否匹配,都可选择不匹配 s3dp[i][j][k] = max(dp[i][j][k], dp[i-1][j-1][k] + 1)
  2. 若字符不相等:
    • 继承 s1s2 缩短一个字符的最大值:dp[i][j][k] = max(dp[i-1][j][k], dp[i][j-1][k])

初始化

  • dp[0][j][k] = dp[i][0][k] = -INF(无效值,因为无法从空串匹配非空 s3
  • 例外:k = 0 时,表示尚未开始匹配 s3,此时长度为 0,即 dp[i][j][0] = 0

最终答案

  • dp[len(s1)][len(s2)][len(s3)],若为负则返回 0。

详细步骤

  1. 定义 DP 数组

    • 维度:(len(s1)+1) x (len(s2)+1) x (len(s3)+1)
    • 初始化所有值为 -10**9(表示不可能状态)。
    • 对于所有 i, j,设 dp[i][j][0] = 0(允许公共子序列不匹配任何 s3 的字符)。
  2. 三重循环迭代

    for i in range(1, len(s1)+1):  
      for j in range(1, len(s2)+1):  
        for k in range(0, len(s3)+1):  
          # 情况1:s1[i-1] == s2[j-1]  
          if s1[i-1] == s2[j-1]:  
            # 选项1:当前字符用于匹配 s3 的第 k 个字符(需 k>0 且 s1[i-1]==s3[k-1])  
            if k > 0 and s1[i-1] == s3[k-1]:  
              dp[i][j][k] = max(dp[i][j][k], dp[i-1][j-1][k-1] + 1)  
            # 选项2:当前字符不用于匹配 s3(即保持 k 不变)  
            dp[i][j][k] = max(dp[i][j][k], dp[i-1][j-1][k] + 1)  
          # 情况2:字符不相等,继承左侧或上侧  
          dp[i][j][k] = max(dp[i][j][k], dp[i-1][j][k], dp[i][j-1][k])  
    
  3. 结果提取

    • dp[n][m][len(s3)] < 0,说明无法包含完整 s3,返回 0;否则返回该值。

示例验证
s1 = "abcde", s2 = "ace", s3 = "ae" 为例:

  • 最终状态 dp[5][3][2] 的计算路径:
    • 匹配 'a'k=1)后,继续匹配 'e'k=2),得到长度 3。
  • 输出 3,符合预期。

该方法确保了在 O(nml) 时间内求解,兼顾了 LCS 和子序列包含约束。

最长公共子序列的变种:带限制的最长公共子序列 题目描述 给定三个字符串 s1 、 s2 和 s3 ,求 s1 和 s2 的最长公共子序列(LCS),且要求这个公共子序列中必须包含 s3 作为其子序列(即 s3 是 LCS 的一个连续或非连续子序列)。如果不存在满足条件的公共子序列,返回 0。 例如: 输入: s1 = "abcde" , s2 = "ace" , s3 = "ae" 输出:3(LCS 为 "ace" ,包含 "ae" ) 输入: s1 = "abcde" , s2 = "ace" , s3 = "af" 输出:0(LCS "ace" 不包含 "af" ) 解题思路 本题是 LCS 的扩展,需在常规 LCS 的动态规划过程中增加对 s3 的约束。我们可以分两步处理: 先求 s1 和 s2 的 LCS (经典 DP)。 检查 LCS 是否包含 s3 ,但直接提取 LCS 再判断会超时(LCS 可能指数级数量),因此需在 DP 过程中同时追踪与 s3 的匹配情况。 改进方法 : 定义三维 DP 数组 dp[i][j][k] : 表示 s1 的前 i 个字符、 s2 的前 j 个字符的公共子序列中,匹配到 s3 的前 k 个字符时的 最长公共子序列长度 。 若 k = len(s3) ,说明已完全包含 s3 。 状态转移 : 若 s1[i-1] == s2[j-1] : 字符可加入公共子序列,检查它是否与 s3[k] 匹配: 若匹配,则 k 可增加 1: dp[i][j][k] = max(dp[i][j][k], dp[i-1][j-1][k-1] + 1) 无论是否匹配,都可选择不匹配 s3 : dp[i][j][k] = max(dp[i][j][k], dp[i-1][j-1][k] + 1) 若字符不相等: 继承 s1 或 s2 缩短一个字符的最大值: dp[i][j][k] = max(dp[i-1][j][k], dp[i][j-1][k]) 初始化 : dp[0][j][k] = dp[i][0][k] = -INF (无效值,因为无法从空串匹配非空 s3 ) 例外: k = 0 时,表示尚未开始匹配 s3 ,此时长度为 0,即 dp[i][j][0] = 0 。 最终答案 : dp[len(s1)][len(s2)][len(s3)] ,若为负则返回 0。 详细步骤 定义 DP 数组 维度: (len(s1)+1) x (len(s2)+1) x (len(s3)+1) 初始化所有值为 -10**9 (表示不可能状态)。 对于所有 i, j ,设 dp[i][j][0] = 0 (允许公共子序列不匹配任何 s3 的字符)。 三重循环迭代 结果提取 若 dp[n][m][len(s3)] < 0 ,说明无法包含完整 s3 ,返回 0;否则返回该值。 示例验证 以 s1 = "abcde" , s2 = "ace" , s3 = "ae" 为例: 最终状态 dp[5][3][2] 的计算路径: 匹配 'a' ( k=1 )后,继续匹配 'e' ( k=2 ),得到长度 3。 输出 3,符合预期。 该方法确保了在 O(n m l) 时间内求解,兼顾了 LCS 和子序列包含约束。