线性动态规划:最长公共子序列的变种——带通配符的最长公共子序列(进阶版:通配符可匹配任意长度子串)
字数 1149 2025-11-05 08:30:58
线性动态规划:最长公共子序列的变种——带通配符的最长公共子序列(进阶版:通配符可匹配任意长度子串)
题目描述
给定两个字符串 s1 和 s2,其中 s1 可能包含通配符 *,* 可以匹配任意长度的子串(包括空串)。要求找出 s2 的一个子序列,使得该子序列与 s1 去掉通配符后的字符顺序一致,且通配符匹配的子串在 s2 中连续出现。求匹配成功时 s2 中最长子序列的长度。
示例
- 输入:
s1 = "a*b", s2 = "acccb" - 输出:
4(解释:*匹配"ccc",子序列"a" + "ccc" + "b"在s2中连续,但要求是子序列,实际匹配"a"、"ccc"、"b"在s2中按顺序出现即可,连续性是针对*匹配的部分)
解题步骤
-
问题分析
- 通配符
*可匹配任意长度子串(包括空串),但匹配的部分在s2中必须连续。 - 目标:在
s2中按顺序匹配s1的非通配符字符,并最大化匹配长度。
- 通配符
-
状态定义
- 设
dp[i][j]表示s1的前i个字符与s2的前j个字符匹配时,s2中已匹配的最长子序列长度。 - 注意:
s1中的通配符不直接参与匹配,而是影响匹配方式。
- 设
-
状态转移方程
- 当
s1[i-1]不是通配符时:- 若
s1[i-1] == s2[j-1],则dp[i][j] = max(dp[i][j], dp[i-1][j-1] + 1) - 否则,
dp[i][j] = dp[i][j-1](跳过s2的当前字符)
- 若
- 当
s1[i-1]是通配符*时:*可以匹配s2中从当前位置开始的任意长度子串(包括空串)。- 转移方式:
dp[i][j] = max(dp[i-1][k]),其中k从0到j(表示*匹配s2中从k到j的子串) - 优化:可通过维护前缀最大值避免遍历
k。
- 当
-
初始化
dp[0][j] = 0(s1为空时匹配长度为 0)dp[i][0] = 0(s2为空时无法匹配非空s1)
-
计算顺序
- 外层循环遍历
s1的每个字符,内层循环遍历s2的每个字符。 - 遇到通配符时,使用辅助数组记录前缀最大值以优化计算。
- 外层循环遍历
-
结果提取
- 最终答案为
dp[len(s1)][len(s2)],表示整个s1与s2匹配的最长子序列长度。
- 最终答案为
关键点
- 通配符
*的匹配需通过遍历所有可能的匹配区间实现,利用动态规划的前缀优化降低复杂度。 - 重点区分通配符匹配的连续性与子序列的顺序性。