线性动态规划:最长公共子序列的变种——带字符必须按顺序出现限制的最长公共子序列(进阶版:限制某些字符必须连续出现k次,且总出现次数有限制)
题目描述
给定两个字符串 s 和 t,以及一个字符集合 C(例如 C = {'a', 'b'})和一个整数 k(k ≥ 1)。要求找到 s 和 t 的一个最长公共子序列(LCS),满足以下条件:
- 子序列中所有属于
C的字符必须按顺序连续出现至少k次(即每个c ∈ C在子序列中出现的连续段长度必须 ≥k)。 - 子序列中每个字符
c ∈ C的总出现次数不能超过给定的上限(例如max_count[c])。
例如,若 s = "aabbcc", t = "abcabc", C = {'a', 'b'}, k = 2, max_count = {'a': 3, 'b': 2},则满足条件的 LCS 可能是 "aabb"(其中 'a' 连续出现 2 次,'b' 连续出现 2 次,且 'a' 总次数 2 ≤ 3,'b' 总次数 2 ≤ 2)。
解题过程
步骤 1:定义状态
在标准 LCS 的二维动态规划基础上,需要增加状态来跟踪 C 中字符的连续出现次数和总出现次数。
定义 dp[i][j][x][y]:
i:考虑s的前i个字符(1-based indexing)。j:考虑t的前j个字符。x:当前子序列末尾字符的连续出现次数(若末尾字符不属于C,则x = 0)。y:一个位掩码或整数,记录各c ∈ C的总出现次数(由于上限较小,可用多维数组或压缩状态)。
但直接这样定义状态空间可能过大。我们需要优化:
- 仅当当前字符属于
C时才记录连续次数,否则连续次数为 0。 - 总次数限制可以通过预处理或状态转移时检查是否超限来避免记录全部组合。
简化状态设计:
设 dp[i][j][last_char][cont_count][total_count],其中:
last_char:当前子序列末尾字符(用整数表示,0 表示无字符或字符不属于C)。cont_count:当前连续次数(仅当last_char ∈ C时有效)。total_count:各c ∈ C的总次数数组(可压缩为整数,若上限小)。
但维度仍太高。进一步优化:
- 将
total_count提取到外部循环,通过预处理所有可能的总次数组合,对每种组合单独做 DP。 - 状态简化为
dp[i][j][last_char][cont_count],在转移时检查总次数是否超限。
步骤 2:状态转移方程
对于每个 (i, j) 和状态 (last_char, cont_count):
- 不选
s[i]或t[j]:
dp[i][j][lc][cc] = max(dp[i-1][j][lc][cc], dp[i][j-1][lc][cc])。 - 当
s[i] == t[j]时,考虑将s[i]加入子序列:- 若
s[i] ∉ C:
新状态(last_char = 0, cont_count = 0),总次数不变。
dp[i][j][0][0] = max(dp[i][j][0][0], dp[i-1][j-1][lc][cc] + 1)。 - 若
s[i] ∈ C:- 若
last_char == s[i]:
新连续次数new_cc = cont_count + 1。
检查new_cc是否满足new_cc >= k或new_cc == 1(允许刚开始的连续段)。
总次数new_total = total_count[s[i]] + 1不能超限。
dp[i][j][s[i]][new_cc] = max(...)。 - 若
last_char != s[i]:
新连续次数new_cc = 1。
检查总次数是否超限。
dp[i][j][s[i]][1] = max(...)。
- 若
- 若
步骤 3:初始化
dp[0][j][0][0] = 0对所有j。dp[i][0][0][0] = 0对所有i。- 其他状态初始为
-inf(表示不可达)。
步骤 4:答案提取
遍历所有 i = |s|, j = |t|,以及所有 (last_char, cont_count),满足:
- 若
last_char ∈ C,则cont_count >= k(保证末尾连续段也满足条件)。 - 各字符总次数不超过上限。
取最大值即为最长公共子序列长度。
步骤 5:复杂度优化
状态数 O(n * m * |C| * k)(因为连续次数只需记录到 k,超过 k 可合并为 k)。
总复杂度 O(n * m * |C| * k),在 |C| 和 k 较小时可行。
示例验证
以 s = "aabb", t = "abab", C = {'a','b'}, k = 2, max_count = {'a':2, 'b':2} 为例:
- 可能 LCS:
"aabb"(长度 4,满足连续 ≥2 且总次数未超限)。 - 通过 DP 计算可得最大长度为 4。
通过以上步骤,我们解决了带复杂限制的 LCS 变种问题。