线性动态规划:最长公共子序列的变种——带通配符的最长公共子序列(进阶版:通配符可匹配任意长度子串)
字数 1267 2025-12-04 15:13:53
线性动态规划:最长公共子序列的变种——带通配符的最长公共子序列(进阶版:通配符可匹配任意长度子串)
题目描述
给定两个字符串 s1 和 s2,其中 s2 可能包含通配符 *,它可以匹配任意长度的子串(包括空串)。要求找出 s1 和 s2 的最长公共子序列(LCS),但需满足通配符的匹配规则。例如:
- 输入:
s1 = "abcde",s2 = "a*c*e" - 输出:
5(LCS 为"abcde",因为*可匹配"b"和"d"之间的子串)。
解题过程
-
定义状态
设dp[i][j]表示s1的前i个字符与s2的前j个字符的带通配符的 LCS 长度。i的范围是[0, len(s1)],j的范围是[0, len(s2)]。- 初始条件:
dp[0][j]和dp[i][0]需根据通配符规则初始化。
-
处理通配符的初始化
- 若
s2的开头有连续通配符*,则dp[0][j]可能不为 0(因为*可匹配空串)。
具体规则:dp[0][0] = 0(两个空串的 LCS 长度为 0)。- 对于
j >= 1,若s2[j-1]是*,则dp[0][j] = dp[0][j-1](通配符匹配空串);否则dp[0][j] = -∞(表示不匹配)。
- 对于
i >= 1,dp[i][0] = 0(s2为空时只能匹配空串)。
- 若
-
状态转移方程
考虑s1[i-1]和s2[j-1]的匹配情况:-
Case 1:
s2[j-1]是通配符*
*可匹配空串、单个字符或任意长子串,因此:dp[i][j] = max( dp[i][j-1], // * 匹配空串 dp[i-1][j], // * 匹配 s1[i-1] 并继续匹配更长部分 dp[i-1][j-1] + 1 // * 匹配单个字符 s1[i-1](可选情况,但已被前两种情况覆盖) )注意:
dp[i-1][j]已隐含了*匹配多个字符的情况(通过递归地匹配s1的前缀)。 -
Case 2:
s2[j-1]不是通配符
若s1[i-1] == s2[j-1],则dp[i][j] = dp[i-1][j-1] + 1;
否则,dp[i][j] = max(dp[i-1][j], dp[i][j-1])(标准 LCS 的不匹配情况)。
-
-
递推顺序与最终结果
- 按
i从 1 到len(s1),j从 1 到len(s2)顺序递推。 - 最终结果为
dp[len(s1)][len(s2)]。
- 按
-
示例验证
以s1 = "abcde",s2 = "a*c*e"为例:- 初始化后,
dp表首行首列均为 0(因s2以*开头,dp[0][1]到dp[0][5]均为 0)。 - 递推后,
dp[5][5] = 5,匹配整个s1。
- 初始化后,
关键点
- 通配符
*的匹配通过dp[i][j-1](匹配空)和dp[i-1][j](匹配多个字符)覆盖。 - 时间复杂度 O(nm),空间复杂度可优化至 O(min(n, m))。