线性动态规划:最长公共子序列的变种——带字符必须按顺序出现限制的最长公共子序列(进阶版:限制某些字符必须连续出现k次)
字数 1271 2025-11-20 06:18:07

线性动态规划:最长公共子序列的变种——带字符必须按顺序出现限制的最长公共子序列(进阶版:限制某些字符必须连续出现k次)

题目描述

给定两个字符串s和t,以及一个字符集C和整数k,要求找到s和t的最长公共子序列,满足:

  1. 对于字符集C中的每个字符,如果在子序列中出现,则必须连续出现至少k次
  2. 子序列中字符的相对顺序与在s和t中相同

例如:
s = "aabcbc", t = "abcabc", C = {'b'}, k = 2
最长公共子序列为"abbc"(b连续出现2次)

解题思路

步骤1:理解问题本质

这是一个带限制条件的最长公共子序列(LCS)问题。我们需要在标准的LCS动态规划基础上,增加对特定字符连续出现次数的限制。

步骤2:定义状态

定义dp[i][j][c][cnt]表示:

  • 考虑s的前i个字符和t的前j个字符
  • 当前正在处理的字符是c(如果当前没有处理字符,则为空)
  • 当前字符c已经连续出现了cnt次

由于状态空间较大,我们需要仔细设计状态表示。

步骤3:状态转移方程

对于每个状态dp[i][j][c][cnt],考虑三种情况:

  1. 不选s[i]和t[j]
    dp[i][j][c][cnt] = max(dp[i][j][c][cnt], dp[i-1][j][c][cnt], dp[i][j-1][c][cnt])

  2. s[i] == t[j]时选择该字符

    • 如果当前没有处理字符(c为空):
      dp[i][j][s[i]][1] = max(dp[i][j][s[i]][1], dp[i-1][j-1][空字符][0] + 1)

    • 如果当前字符与s[i]相同:
      dp[i][j][c][cnt+1] = max(dp[i][j][c][cnt+1], dp[i-1][j-1][c][cnt] + 1)

    • 如果当前字符与s[i]不同,且cnt >= k(满足连续出现条件):
      dp[i][j][s[i]][1] = max(dp[i][j][s[i]][1], dp[i-1][j-1][c][cnt] + 1)

  3. 边界条件
    dp[0][0][空字符][0] = 0

步骤4:优化状态表示

由于直接使用四维DP可能空间太大,我们可以优化:

  • 只记录当前正在处理的字符和计数
  • 使用两个二维数组交替计算

步骤5:具体实现

def longestCommonSubsequenceWithConstraint(s, t, C, k):
    m, n = len(s), len(t)
    
    # dp[i][j] = (current_char, count, length)
    # 使用字典来存储不同的状态
    dp_prev = {('', 0): 0}  # (当前字符, 连续次数): 长度
    
    for i in range(m + 1):
        dp_curr = {}
        for j in range(n + 1):
            if i == 0 and j == 0:
                dp_curr[('', 0)] = 0
                continue
            
            # 不选s[i-1]或t[j-1]
            for state in dp_prev:
                dp_curr[state] = max(dp_curr.get(state, 0), dp_prev[state])
            
            if i > 0 and j > 0 and s[i-1] == t[j-1]:
                char = s[i-1]
                
                for (curr_char, count), length in dp_prev.items():
                    # 情况1: 开始新的字符序列
                    if curr_char == '':
                        dp_curr[(char, 1)] = max(dp_curr.get((char, 1), 0), length + 1)
                    
                    # 情况2: 延续当前字符序列
                    elif curr_char == char:
                        dp_curr[(char, count + 1)] = max(dp_curr.get((char, count + 1), 0), length + 1)
                    
                    # 情况3: 切换字符(只有满足条件时才允许)
                    elif count >= k or curr_char not in C:
                        dp_curr[(char, 1)] = max(dp_curr.get((char, 1), 0), length + 1)
        
        dp_prev = dp_curr
    
    # 处理最终状态,确保所有字符序列都满足条件
    result = 0
    for (curr_char, count), length in dp_prev.items():
        if curr_char == '' or count >= k or curr_char not in C:
            result = max(result, length)
    
    return result

步骤6:验证示例

对于s = "aabcbc", t = "abcabc", C = {'b'}, k = 2:

  • 有效子序列:"abbc"(长度4),其中b连续出现2次
  • 无效子序列:"abc"(长度3),其中b没有连续出现至少2次

步骤7:复杂度分析

  • 时间复杂度:O(m × n × S),其中S是状态数量
  • 空间复杂度:O(m × n × S)

这个解法通过动态规划状态的设计,巧妙地处理了字符必须连续出现的限制条件,是标准LCS问题的有趣变种。

线性动态规划:最长公共子序列的变种——带字符必须按顺序出现限制的最长公共子序列(进阶版:限制某些字符必须连续出现k次) 题目描述 给定两个字符串s和t,以及一个字符集C和整数k,要求找到s和t的最长公共子序列,满足: 对于字符集C中的每个字符,如果在子序列中出现,则必须连续出现至少k次 子序列中字符的相对顺序与在s和t中相同 例如: s = "aabcbc", t = "abcabc", C = {'b'}, k = 2 最长公共子序列为"abbc"(b连续出现2次) 解题思路 步骤1:理解问题本质 这是一个带限制条件的最长公共子序列(LCS)问题。我们需要在标准的LCS动态规划基础上,增加对特定字符连续出现次数的限制。 步骤2:定义状态 定义dp[ i][ j][ c][ cnt ]表示: 考虑s的前i个字符和t的前j个字符 当前正在处理的字符是c(如果当前没有处理字符,则为空) 当前字符c已经连续出现了cnt次 由于状态空间较大,我们需要仔细设计状态表示。 步骤3:状态转移方程 对于每个状态dp[ i][ j][ c][ cnt ],考虑三种情况: 不选s[ i]和t[ j] dp[ i][ j][ c][ cnt] = max(dp[ i][ j][ c][ cnt], dp[ i-1][ j][ c][ cnt], dp[ i][ j-1][ c][ cnt ]) s[ i] == t[ j]时选择该字符 如果当前没有处理字符(c为空): dp[ i][ j][ s[ i]][ 1] = max(dp[ i][ j][ s[ i]][ 1], dp[ i-1][ j-1][ 空字符][ 0 ] + 1) 如果当前字符与s[ i ]相同: dp[ i][ j][ c][ cnt+1] = max(dp[ i][ j][ c][ cnt+1], dp[ i-1][ j-1][ c][ cnt ] + 1) 如果当前字符与s[ i ]不同,且cnt >= k(满足连续出现条件): dp[ i][ j][ s[ i]][ 1] = max(dp[ i][ j][ s[ i]][ 1], dp[ i-1][ j-1][ c][ cnt ] + 1) 边界条件 dp[ 0][ 0][ 空字符][ 0 ] = 0 步骤4:优化状态表示 由于直接使用四维DP可能空间太大,我们可以优化: 只记录当前正在处理的字符和计数 使用两个二维数组交替计算 步骤5:具体实现 步骤6:验证示例 对于s = "aabcbc", t = "abcabc", C = {'b'}, k = 2: 有效子序列:"abbc"(长度4),其中b连续出现2次 无效子序列:"abc"(长度3),其中b没有连续出现至少2次 步骤7:复杂度分析 时间复杂度:O(m × n × S),其中S是状态数量 空间复杂度:O(m × n × S) 这个解法通过动态规划状态的设计,巧妙地处理了字符必须连续出现的限制条件,是标准LCS问题的有趣变种。