最长公共子序列的变种:带字符权重的最长公共子序列(进阶版:允许负权重且要求子序列权重和非负,同时限制某些字符必须连续出现)
我将为你详细讲解这个线性动态规划问题。题目描述如下:给定两个字符串s和t,每个字符都有一个权重(可能为负),要求找到s和t的一个公共子序列,使得该子序列的权重和最大(非负),并且满足某些特定字符在子序列中必须连续出现(即如果某个特定字符出现在子序列中,那么它在原字符串中连续出现的部分必须全部包含在子序列中)。
问题分析
这是一个带有多重限制条件的最长公共子序列变种问题。我们需要在标准LCS的基础上考虑字符权重、权重和非负的要求,以及特定字符的连续性限制。
关键点分析
- 字符权重:每个字符对总权重有正或负的贡献
- 权重和非负:最终子序列的总权重必须≥0
- 连续性限制:某些字符如果出现在子序列中,必须连续出现
- 公共子序列:必须同时是s和t的子序列
动态规划解法
步骤1:定义状态
我们定义dp[i][j][k]表示考虑s的前i个字符和t的前j个字符,当前子序列的权重和为k时的最长公共子序列长度。
由于权重可能为负,我们需要对权重进行偏移处理。设W为所有权重绝对值的和,则k的范围是[0, 2W]。
步骤2:连续性限制处理
对于必须连续出现的字符,我们需要特殊处理。定义连续字符集合C,对于每个c∈C:
- 如果决定包含c,必须包含c在当前匹配位置的所有连续出现
步骤3:状态转移方程
基础情况:
- dp[0][j][0] = 0(空子序列)
- dp[i][0][0] = 0(空子序列)
一般情况(i>0, j>0):
-
不包含当前字符:
dp[i][j][k] = max(dp[i][j][k], dp[i-1][j][k], dp[i][j-1][k]) -
当s[i] = t[j]时:
-
如果s[i]是必须连续出现的字符:
- 找到s中从i开始向前的连续s[i]字符段
- 找到t中从j开始向前的连续s[i]字符段
- 设连续段长度分别为L_s和L_t,取最小值L = min(L_s, L_t)
- 计算连续段的权重和w_cont
- 对于所有可能的k',如果k' + w_cont ≥ 0:
dp[i][j][k' + w_cont] = max(dp[i][j][k' + w_cont], dp[i-L][j-L][k'] + L)
-
如果s[i]不是必须连续出现的字符:
- w = weight[s[i]]
- 对于所有可能的k',如果k' + w ≥ 0:
dp[i][j][k' + w] = max(dp[i][j][k' + w], dp[i-1][j-1][k'] + 1)
-
步骤4:边界情况处理
- 需要确保索引不越界
- 权重和始终保持在有效范围内[0, 2W]
步骤5:结果提取
最终结果是所有dp[n][m][k]中的最大值,其中n和m分别是s和t的长度,k≥0。
优化技巧
- 使用滚动数组优化空间复杂度
- 只记录可能达到的权重状态
- 预处理连续段信息
这个算法的时间复杂度是O(n×m×W),空间复杂度可以通过优化降到O(m×W)。通过这种细致的状态设计,我们能够同时处理权重约束和连续性限制,找到满足所有条件的最优解。