线性动态规划:最长有效括号匹配子序列的变种——允许最多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(没有未匹配的左括号):
这个右括号无法匹配,只能跳过
- 如果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。
这个算法能够有效处理允许有限次失配的最长有效括号子序列问题,在实际应用中具有很好的实用性。