线性动态规划:正则表达式匹配
字数 1786 2025-11-01 09:19:03
线性动态规划:正则表达式匹配
题目描述
给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。
'.'匹配任意单个字符。'*'匹配零个或多个前面的那一个元素。
匹配应覆盖整个字符串s,而不是部分字符串。
例如:s = "aa", p = "a"→false(p未匹配整个s)s = "aa", p = "a*"→true(*匹配零个或多个a)s = "ab", p = ".*"→true(.*表示匹配任意字符零次或多次)
解题过程
1. 定义状态
设 dp[i][j] 表示 s 的前 i 个字符(即 s[0...i-1])与 p 的前 j 个字符(即 p[0...j-1])是否匹配。
i和j分别表示s和p的字符数量,从 0 到len(s)和len(p)。- 最终目标是求
dp[len(s)][len(p)]。
2. 初始化
dp[0][0] = true:空字符串与空模式匹配。- 对于
j > 0,dp[0][j]表示空字符串能否被p的前j个字符匹配。只有当p的某些部分形如a*b*c*(即偶数位置为*且可匹配零次)时才可能为true。具体规则:- 若
p[j-1] == '*',则dp[0][j] = dp[0][j-2](跳过x*部分)。 - 否则为
false。
- 若
3. 状态转移方程
遍历 i 从 1 到 len(s),j 从 1 到 len(p):
-
情况1:
p[j-1]是普通字符或'.'- 若
s[i-1] == p[j-1]或p[j-1] == '.',则当前字符匹配,状态继承自前一部分:
dp[i][j] = dp[i-1][j-1]。 - 否则
dp[i][j] = false。
- 若
-
情况2:
p[j-1] == '*'- 此时
*必须与它前面的字符p[j-2]结合(记为char*组合)。 - 子情况2.1:匹配零次
- 忽略
char*组合,即dp[i][j] = dp[i][j-2]。
- 忽略
- 子情况2.2:匹配一次或多次
- 前提:
s[i-1]与p[j-2]匹配(即s[i-1] == p[j-2]或p[j-2] == '.')。 - 此时
s的当前字符可由char*匹配,问题转化为s的前i-1个字符是否与p的前j个字符匹配(因为*可继续匹配前面的字符):
dp[i][j] = dp[i-1][j]。
- 前提:
- 综合两种情况:
dp[i][j] = dp[i][j-2] || (匹配当前字符 && dp[i-1][j])。
- 此时
4. 示例推导
以 s = "aab", p = "c*a*b" 为例(应返回 true):
- 初始化:
dp[0][0] = true;dp[0][1] = false(p[0]='c');dp[0][2] = dp[0][0]=true(跳过c*);dp[0][3]=false;dp[0][4]=dp[0][2]=true(跳过a*);dp[0][5]=false。 - 填表过程(简略):
i=1, j=1:p[0]='c'不匹配s[0]='a',且p[1]!='*',故false。i=1, j=2:p[1]='*',匹配零次时dp[1][2] = dp[1][0]=false;匹配一次时需s[0]匹配c,不成立,故false。- 逐步计算至
i=3, j=5:最终dp[3][5] = true。
5. 复杂度分析
- 时间复杂度:O(mn),其中
m和n分别为s和p的长度。 - 空间复杂度:可优化至 O(n)(使用滚动数组)。
关键点
- 处理
*时需考虑匹配零次或多次,并利用已计算的子问题结果。 - 初始化空字符串的匹配情况是易错点。