线性动态规划:最长公共子序列的变种——带字符出现次数限制的最长公共子序列(进阶版:限制某些字符必须连续出现k次)
字数 1261 2025-11-08 10:02:38
线性动态规划:最长公共子序列的变种——带字符出现次数限制的最长公共子序列(进阶版:限制某些字符必须连续出现k次)
题目描述
给定两个字符串 s 和 t,以及一个整数 k。要求找到 s 和 t 的最长公共子序列(LCS),但附加条件:在公共子序列中,如果某个字符连续出现,则必须至少连续出现 k 次(即不允许出现连续次数少于 k 的相同字符段)。例如,若 k=3,则子序列中 "aa" 是无效的(因为连续出现2次 < 3),但 "aaa" 或 "aaaa" 是有效的。
解题过程
-
问题分析
传统 LCS 使用动态规划定义dp[i][j]表示s[0..i-1]和t[0..j-1]的 LCS 长度。本题需额外记录连续字符的出现次数,并确保连续段长度 ≥ k。 -
状态设计
定义dp[i][j][c][cnt]:- 考虑
s的前i个字符和t的前j个字符。 c表示当前公共子序列的最后一个字符(用整数表示,如 'a'=0, 'b'=1...),若暂无字符则设为特殊值(如 -1)。cnt表示字符c在当前公共子序列末尾的连续出现次数。
状态值表示满足条件的最长子序列长度。
- 考虑
-
状态转移
- 情况1:不选
s[i-1]或t[j-1]
继承状态:dp[i][j][c][cnt] = max(dp[i-1][j][c][cnt], dp[i][j-1][c][cnt])。 - 情况2:
s[i-1] == t[j-1],且匹配该字符
设当前字符ch = s[i-1]:- 若
ch != c(新字符段):需检查旧段是否有效(若c != -1且cnt < k,则旧段无效,不能转移);有效时新段长度=1。 - 若
ch == c(延续旧段):新连续次数new_cnt = cnt + 1。
转移方程:
if ch == c: dp[i][j][c][new_cnt] = max(dp[i][j][c][new_cnt], dp[i-1][j-1][c][cnt] + 1) else: if c == -1 or cnt >= k: # 旧段有效或无旧段 dp[i][j][ch][1] = max(dp[i][j][ch][1], dp[i-1][j-1][c][cnt] + 1) - 若
- 情况1:不选
-
初始化和答案提取
- 初始化:
dp[0][0][-1][0] = 0表示空子序列。 - 答案:所有
dp[len(s)][len(t)][c][cnt]的最大值,且需满足cnt >= k或c == -1(空序列)。
- 初始化:
-
优化
状态数可能较大,可滚动数组优化空间。实际实现时需注意无效状态的剪枝(如cnt > k时只需保留cnt = k,因为更长连续段不影响后续转移)。
示例
设 s = "aabba", t = "aacaa", k=2。
有效 LCS 示例:"aaa"(连续3次 ≥2),无效示例:"aa"(虽连续但次数=2? 注意:2≥2 有效,但若取 "aa" 则长度短于 "aaa")。
通过 DP 计算可得最长有效子序列为 "aaa" 或 "aa"(但 "aa" 长度较短),最终答案为 3。
总结
本题通过扩展 LCS 状态,加入末尾字符及其连续次数,确保了连续段长度的限制。关键点在于正确处理字符段切换时的有效性检查。