最长公共子序列的变种:带字符替换限制的最长公共子序列(进阶版:允许部分字符替换为通配符)
字数 1068 2025-11-02 19:16:02
最长公共子序列的变种:带字符替换限制的最长公共子序列(进阶版:允许部分字符替换为通配符)
题目描述
给定两个字符串s和t,以及一个字符替换规则:某些特定字符可以被替换为通配符'?'(通配符可以匹配任意字符)。求在允许进行字符替换操作后,s和t的最长公共子序列(LCS)长度。注意:每个字符最多只能被替换一次,且只能替换为通配符。
解题过程
步骤1:问题分析
这是标准LCS问题的变种,关键区别在于允许将特定字符替换为通配符。通配符可以匹配任意字符,这增加了匹配的灵活性。我们需要在动态规划状态中记录替换操作的使用情况。
步骤2:状态定义
定义三维DP数组:
dp[i][j][k]表示考虑字符串s的前i个字符和t的前j个字符,在使用k次替换操作的情况下,能得到的最长公共子序列长度。- 其中k的取值范围为0到最大允许替换次数(本题中为特定字符的数量)。
步骤3:状态转移方程
考虑字符匹配的几种情况:
-
不匹配任何字符:
dp[i][j][k] = max(dp[i-1][j][k], dp[i][j-1][k]) -
字符直接匹配(s[i-1] == t[j-1]):
dp[i][j][k] = max(dp[i][j][k], dp[i-1][j-1][k] + 1) -
使用通配符匹配:
- 如果s[i-1]可以被替换为通配符:
dp[i][j][k] = max(dp[i][j][k], dp[i-1][j-1][k-1] + 1)(当k > 0时) - 如果t[j-1]可以被替换为通配符:
dp[i][j][k] = max(dp[i][j][k], dp[i-1][j-1][k-1] + 1)(当k > 0时)
- 如果s[i-1]可以被替换为通配符:
步骤4:初始化
dp[0][j][k] = 0(空字符串与任何字符串的LCS为0)dp[i][0][k] = 0dp[0][0][k] = 0
步骤5:算法实现
def lcs_with_replacement(s, t, replaceable_chars):
m, n = len(s), len(t)
max_k = sum(1 for c in s if c in replaceable_chars) + \
sum(1 for c in t if c in replaceable_chars)
dp = [[[0] * (max_k + 1) for _ in range(n + 1)] for _ in range(m + 1)]
for i in range(1, m + 1):
for j in range(1, n + 1):
for k in range(max_k + 1):
# 情况1:不匹配当前字符
dp[i][j][k] = max(dp[i-1][j][k], dp[i][j-1][k])
# 情况2:直接匹配
if s[i-1] == t[j-1]:
dp[i][j][k] = max(dp[i][j][k], dp[i-1][j-1][k] + 1)
# 情况3:使用通配符匹配
if k > 0:
if s[i-1] in replaceable_chars:
dp[i][j][k] = max(dp[i][j][k], dp[i-1][j-1][k-1] + 1)
if t[j-1] in replaceable_chars:
dp[i][j][k] = max(dp[i][j][k], dp[i-1][j-1][k-1] + 1)
return max(dp[m][n])
步骤6:复杂度分析
- 时间复杂度:O(m × n × K),其中K为最大替换次数
- 空间复杂度:O(m × n × K),可通过滚动数组优化到O(n × K)
步骤7:示例验证
设s = "ABC", t = "ADC", 可替换字符集 = {'B', 'D'}
- 最大替换次数K = 2(s中有1个可替换字符,t中有1个)
- 最优解:将B替换为通配符,匹配t的D;或者将D替换为通配符,匹配s的B
- LCS长度 = 2("AC")
这个算法通过三维动态规划有效处理了字符替换的限制条件,扩展了标准LCS的应用范围。