最长公共子序列的变种:带字符替换限制的最长公共子序列(进阶版:允许部分字符替换为通配符)
字数 1093 2025-11-05 23:45:49
最长公共子序列的变种:带字符替换限制的最长公共子序列(进阶版:允许部分字符替换为通配符)
题目描述
给定两个字符串 s1 和 s2,以及一个字符替换规则:允许将 s1 中的某些特定字符(称为“可替换字符”)替换为通配符 *,该通配符可以匹配 s2 中的任意字符(包括连续多个字符)。此外,通配符在 s1 中最多连续出现次数有限制(设为 k,即通配符段长度不超过 k)。求在这种替换规则下,s1 和 s2 的最长公共子序列(LCS)长度。
解题过程
-
问题分析
- 常规 LCS 问题中,字符必须精确匹配。
- 本题允许将
s1的部分字符替换为通配符,通配符可匹配s2的任意字符,但通配符段长度受限。 - 目标:利用替换规则最大化 LCS 长度。
-
状态定义
设dp[i][j]表示考虑s1的前i个字符和s2的前j个字符时,能得到的最大 LCS 长度。
额外状态cnt[i][j]表示在dp[i][j]状态下,当前连续通配符段的长度(若末尾非通配符则为 0)。 -
状态转移
- 情况1:精确匹配
若s1[i-1] == s2[j-1],则直接继承dp[i-1][j-1] + 1,且重置通配符计数:dp[i][j] = max(dp[i][j], dp[i-1][j-1] + 1) cnt[i][j] = 0 // 匹配后通配符段中断 - 情况2:使用通配符替换
若s1[i-1]是可替换字符,且当前通配符段长度< k,则可将s1[i-1]替换为通配符,匹配s2[j-1]:
同时,通配符可匹配dp[i][j] = max(dp[i][j], dp[i-1][j-1] + 1) // 通配符匹配一个字符 cnt[i][j] = cnt[i-1][j-1] + 1 // 通配符段延长s2的多个字符,需检查s1前导通配符段:for t in [1, min(k, i)]: // 尝试用长度为t的通配符段匹配s2的末尾字符 if cnt[i-t][j-1] == t: // 前t个字符是通配符段 dp[i][j] = max(dp[i][j], dp[i-t][j-1] + 1) cnt[i][j] = t // 延续通配符段 - 情况3:跳过字符
跳过s1[i-1]或s2[j-1]:dp[i][j] = max(dp[i-1][j], dp[i][j-1]) // 通配符段继承或重置(根据具体选择)
- 情况1:精确匹配
-
初始化
dp[0][j] = 0,dp[i][0] = 0(空字符串无公共子序列)。cnt[0][j] = 0,cnt[i][0] = 0。
-
复杂度优化
- 直接实现复杂度为 O(n³),可通过预处理前导通配符段长度优化到 O(n²)。
-
示例演示
设s1 = "aXb",s2 = "acb", 可替换字符集{X},k=1。- 步骤1:精确匹配
a,dp[1][1]=1。 - 步骤2:
s1[1]='X'可替换,用通配符匹配s2[1]='c',dp[2][2]=2。 - 步骤3:精确匹配
b,dp[3][3]=3。 - 结果:LCS 长度为 3(通配符帮助匹配了
c)。
- 步骤1:精确匹配
关键点
- 通配符的连续使用受长度限制,需通过
cnt状态跟踪。 - 通配符可匹配任意字符,但需在状态转移中合理处理多字符匹配情况。