线性动态规划:最长有效括号匹配子序列的变种——允许最多k次失配的最长有效括号子序列
字数 1333 2025-11-22 00:19:03

线性动态规划:最长有效括号匹配子序列的变种——允许最多k次失配的最长有效括号子序列

让我为您讲解一个线性动态规划问题:在允许最多k次失配的情况下,找到最长的有效括号子序列。

问题描述
给定一个由'('和')'组成的字符串s,以及一个非负整数k。我们需要找到s的一个子序列,使得:

  1. 这个子序列是一个有效的括号序列(即左右括号正确匹配)
  2. 在形成这个子序列的过程中,最多允许跳过k个字符(即最多k次"失配")

解题思路分析
这个问题是经典最长有效括号问题的变种,我们需要在动态规划状态中记录失配次数。

动态规划状态定义
定义dp[i][j][m]表示考虑字符串s的前i个字符,当前未匹配的左括号数量为j,已经使用了m次失配机会时,能够获得的最长有效括号子序列长度。

状态转移方程

  1. 跳过当前字符(使用失配机会)
    如果m < k(还有失配机会),我们可以跳过当前字符:
    dp[i][j][m+1] = max(dp[i][j][m+1], dp[i-1][j][m])

  2. 选择当前字符

    • 如果当前字符是'(':
      选择这个左括号,未匹配左括号数加1:
      dp[i][j+1][m] = max(dp[i][j+1][m], dp[i-1][j][m] + 1)

    • 如果当前字符是')':

      • 如果j > 0(有未匹配的左括号):
        选择这个右括号与前面的左括号匹配,未匹配左括号数减1:
        dp[i][j-1][m] = max(dp[i][j-1][m], dp[i-1][j][m] + 2)
      • 如果j = 0(没有未匹配的左括号):
        这个右括号无法匹配,只能跳过

初始化
dp[0][0][0] = 0(空字符串,没有未匹配括号,没有失配)
其他状态初始化为-∞

最终答案
答案是所有dp[n][0][m]中的最大值,其中0 ≤ m ≤ k

具体示例
考虑字符串s = "())", k = 1

逐步计算:

  • i=1, 字符'(':可以跳过或选择

    • 选择:dp[1][1][0] = 1
    • 跳过:dp[1][0][1] = 0
  • i=2, 字符')':
    从dp[1][1][0]=1出发:

    • 选择:匹配成功,dp[2][0][0] = 1+2 = 3
    • 跳过:dp[2][1][1] = 1

    从dp[1][0][1]=0出发:

    • 选择:无法匹配,只能跳过
    • 跳过:dp[2][0][2] = 0
  • i=3, 字符')':
    从dp[2][0][0]=3出发:

    • 选择:无法匹配,只能跳过
    • 跳过:dp[3][0][1] = 3

    从dp[2][1][1]=1出发:

    • 选择:匹配成功,dp[3][0][1] = max(3, 1+2) = 3
    • 跳过:dp[3][1][2] = 1

最终答案:max(dp[3][0][m]) = 3

时间复杂度优化
由于空间复杂度可能较高,我们可以使用滚动数组优化,将i的维度降为2。

这个算法能够有效处理允许有限次失配的最长有效括号子序列问题,在实际应用中具有很好的实用性。

线性动态规划:最长有效括号匹配子序列的变种——允许最多k次失配的最长有效括号子序列 让我为您讲解一个线性动态规划问题:在允许最多k次失配的情况下,找到最长的有效括号子序列。 问题描述 给定一个由'('和')'组成的字符串s,以及一个非负整数k。我们需要找到s的一个子序列,使得: 这个子序列是一个有效的括号序列(即左右括号正确匹配) 在形成这个子序列的过程中,最多允许跳过k个字符(即最多k次"失配") 解题思路分析 这个问题是经典最长有效括号问题的变种,我们需要在动态规划状态中记录失配次数。 动态规划状态定义 定义dp[ i][ j][ m ]表示考虑字符串s的前i个字符,当前未匹配的左括号数量为j,已经使用了m次失配机会时,能够获得的最长有效括号子序列长度。 状态转移方程 跳过当前字符(使用失配机会) 如果m < k(还有失配机会),我们可以跳过当前字符: dp[ i][ j][ m+1] = max(dp[ i][ j][ m+1], dp[ i-1][ j][ m ]) 选择当前字符 如果当前字符是'(': 选择这个左括号,未匹配左括号数加1: dp[ i][ j+1][ m] = max(dp[ i][ j+1][ m], dp[ i-1][ j][ m ] + 1) 如果当前字符是')': 如果j > 0(有未匹配的左括号): 选择这个右括号与前面的左括号匹配,未匹配左括号数减1: dp[ i][ j-1][ m] = max(dp[ i][ j-1][ m], dp[ i-1][ j][ m ] + 2) 如果j = 0(没有未匹配的左括号): 这个右括号无法匹配,只能跳过 初始化 dp[ 0][ 0][ 0 ] = 0(空字符串,没有未匹配括号,没有失配) 其他状态初始化为-∞ 最终答案 答案是所有dp[ n][ 0][ m ]中的最大值,其中0 ≤ m ≤ k 具体示例 考虑字符串s = "())", k = 1 逐步计算: i=1, 字符'(':可以跳过或选择 选择:dp[ 1][ 1][ 0 ] = 1 跳过:dp[ 1][ 0][ 1 ] = 0 i=2, 字符')': 从dp[ 1][ 1][ 0 ]=1出发: 选择:匹配成功,dp[ 2][ 0][ 0 ] = 1+2 = 3 跳过:dp[ 2][ 1][ 1 ] = 1 从dp[ 1][ 0][ 1 ]=0出发: 选择:无法匹配,只能跳过 跳过:dp[ 2][ 0][ 2 ] = 0 i=3, 字符')': 从dp[ 2][ 0][ 0 ]=3出发: 选择:无法匹配,只能跳过 跳过:dp[ 3][ 0][ 1 ] = 3 从dp[ 2][ 1][ 1 ]=1出发: 选择:匹配成功,dp[ 3][ 0][ 1 ] = max(3, 1+2) = 3 跳过:dp[ 3][ 1][ 2 ] = 1 最终答案:max(dp[ 3][ 0][ m ]) = 3 时间复杂度优化 由于空间复杂度可能较高,我们可以使用滚动数组优化,将i的维度降为2。 这个算法能够有效处理允许有限次失配的最长有效括号子序列问题,在实际应用中具有很好的实用性。