线性动态规划:最长公共子序列的变种——带字符出现次数限制的最长公共子序列(进阶版:限制某些字符必须连续出现k次)
字数 1377 2025-11-27 06:55:50
线性动态规划:最长公共子序列的变种——带字符出现次数限制的最长公共子序列(进阶版:限制某些字符必须连续出现k次)
题目描述
给定两个字符串 s1 和 s2,以及一个整数 k(k ≥ 1)。要求找到 s1 和 s2 的最长公共子序列(LCS),但附加条件为:在公共子序列中,某些指定字符必须连续出现至少 k 次。例如,若指定字符为 'a' 且 k=3,则公共子序列中每个 'a' 必须连续出现至少 3 次(如 "aaa" 是合法的,但 "aa" 不合法)。你需要设计一个动态规划算法,计算满足此条件的最长公共子序列的长度。
解题过程
-
问题分析
- 基础 LCS 使用二维 DP 表
dp[i][j]表示s1[0:i]和s2[0:j]的 LCS 长度。 - 新增限制:某些字符(如 'a')在公共子序列中必须连续出现至少 k 次。这意味着在匹配字符时,需跟踪当前连续匹配的长度。
- 需要扩展状态:除了索引
(i, j),还需记录当前正在匹配的字符ch和连续长度cnt。
- 基础 LCS 使用二维 DP 表
-
状态定义
定义四维 DP 数组:
dp[i][j][ch][cnt]表示考虑s1的前i个字符和s2的前j个字符时,以字符ch结尾且连续出现cnt次的公共子序列的最大长度。- 其中
ch的取值范围为字符集(如小写字母),cnt的范围为1到k。 - 注意:当
cnt < k时,子序列未满足连续条件;仅当cnt >= k时,当前字符段才合法。
- 其中
-
状态转移
- Case 1: 不匹配当前字符
跳过s1[i]或s2[j]:dp[i][j][ch][cnt] = max(dp[i-1][j][ch][cnt], dp[i][j-1][ch][cnt]) - Case 2: 匹配字符(s1[i] == s2[j])
设当前字符c = s1[i]:- 子case 2.1: 延续之前的连续字符
若前一个状态也是字符c且连续长度为cnt-1:dp[i][j][c][cnt] = dp[i-1][j-1][c][cnt-1] + 1 (若 cnt > 1) - 子case 2.2: 开始新的连续段
若前一个状态是其他字符或cnt=1:
其中dp[i][j][c][1] = max(dp[i][j][c][1], max_over_all_ch'{dp[i-1][j-1][ch'][cnt']} + 1)ch'可任意,cnt'需满足cnt' >= k(因为新段开始时,前一段必须已合法)。
- 子case 2.1: 延续之前的连续字符
- 合法性检查:仅当
cnt >= k时,当前状态才计入最终答案。
- Case 1: 不匹配当前字符
-
初始化与边界
- 初始状态:
dp[0][j][ch][cnt] = 0和dp[i][0][ch][cnt] = 0(空字符串无公共子序列)。 - 单独处理
cnt=1的起始情况。
- 初始状态:
-
复杂度优化
- 直接四维 DP 可能开销大(O(n²·|Σ|·k))。可优化:
- 将
(ch, cnt)合并为二维状态,用哈希表或数组压缩。 - 仅对指定字符(如 'a')跟踪
cnt,其他字符视为普通 LCS。
- 将
- 直接四维 DP 可能开销大(O(n²·|Σ|·k))。可优化:
-
示例演示
设s1 = "aabaa",s2 = "aacaaa",k=3,指定字符为 'a'。- 合法公共子序列需包含连续至少 3 个 'a',如 "aaa"。
- 通过 DP 计算,最终找到最长满足条件的子序列为 "aaa"(长度 3)。
总结
本题通过扩展 LCS 的状态维度,加入字符连续出现的计数约束,解决了带限制的公共子序列问题。关键点在于状态转移时区分连续段的延续与新建,并确保只有满足连续条件的段才被采纳。