最长公共子序列的变种:带字符替换限制的最长公共子序列(进阶版:替换操作有不同代价,且允许部分字符替换为通配符,同时限制某些字符必须连续出现)
字数 1277 2025-11-07 22:14:38
最长公共子序列的变种:带字符替换限制的最长公共子序列(进阶版:替换操作有不同代价,且允许部分字符替换为通配符,同时限制某些字符必须连续出现)
题目描述
给定两个字符串 s1 和 s2,以及一个字符替换代价表 cost(cost[a][b] 表示将字符 a 替换为 b 的代价,若为无穷大则禁止替换)。部分字符可替换为通配符 *(匹配任意字符),代价为固定值 c_wild。此外,某些字符被标记为"必须连续出现"(例如,若 s1 中的字符 'x' 被标记,则 'x' 在公共子序列中必须连续出现至少 k 次)。求满足条件的最长公共子序列,且总替换代价最小。
解题过程
-
问题分析
- 核心目标:在字符替换、通配符使用和连续出现限制下,找到最长公共子序列并最小化替换代价。
- 难点:多重约束(替换代价、通配符、连续字符要求)需在动态规划状态中同时考虑。
-
状态设计
定义五维DP数组:
dp[i][j][cnt][last][flag]表示考虑s1的前i个字符和s2的前j个字符时,当前连续字符last已出现cnt次,flag表示是否正在匹配一个连续段(0:未开始,1:进行中)。i:s1的指针(0 ≤ i ≤ len(s1))。j:s2的指针(0 ≤ j ≤ len(s2))。cnt:当前连续字符的计数(0 ≤ cnt ≤ k)。last:上一个匹配的字符(用整数表示,0表示尚未匹配任何字符)。flag:是否处于连续匹配段(0或1)。
-
状态转移
- 字符匹配(不替换):若
s1[i] == s2[j],直接匹配,更新连续计数:- 若
last == s1[i]:cnt_new = cnt + 1(连续计数增加)。 - 否则:
cnt_new = 1(重新开始计数)。
转移代价为0。
- 若
- 字符替换:若
cost[s1[i]][s2[j]]非无穷大,支付代价完成匹配,连续计数更新同上。 - 使用通配符:支付代价
c_wild,将s1[i]或s2[j]替换为*实现匹配,连续计数重置(通配符中断连续性)。 - 跳过字符:跳过
s1[i]或s2[j],连续计数重置为0。 - 连续字符检查:若
cnt == k且last被标记为必须连续,允许结束连续段(flag置0)。
- 字符匹配(不替换):若
-
初始化与结果提取
- 初始化:
dp[0][0][0][0][0] = 0,其余为无穷大。 - 结果:遍历所有
i, j, cnt, last, flag,取dp[i][j][cnt][last][flag]的最小值,且要求flag=0(所有连续段已满足)。
- 初始化:
关键点
- 通过五维状态同时跟踪匹配进度、连续计数和段状态,确保约束被满足。
- 通配符和替换代价在转移时作为可选操作,通过比较代价最小化总成本。
- 时间复杂度 O(n²·k·|Σ|·2),需优化状态空间(如滚动数组)。