LeetCode 10:正则表达式匹配
LeetCode 10:正则表达式匹配
问题本质:精准匹配规则
实现支持 .
(匹配任意单个字符)和 *
(匹配前一个字符0次或多次)的正则表达式,要求完全匹配字符串 s
,而非部分匹配。
核心思路:动态规划(DP)
通过二维 DP 表记录状态:dp[i][j]
表示 s
的前 i
个字符与 p
的前 j
个字符是否匹配。
算法步骤详解
步骤 1:状态定义
创建二维数组 dp
,维度为 (m+1) × (n+1)
(m = s.length()
,n = p.length()
):
dp[i][j]
:s
的前i
个字符(s[0..i-1]
)与p
的前j
个字符(p[0..j-1]
)是否匹配。
步骤 2:初始化边界
- 空字符串匹配:
dp[0][0] = true
(空s
匹配空p
)。 - 处理
p
前导的*
:若p
以a*b*
形式开头,空s
也可匹配。例如:- 当
p[j-1] == '*'
时,dp[0][j] = dp[0][j-2]
(忽略p[j-2]
和p[j-1]
,即*
匹配0次前一个字符)。
- 当
步骤 3:状态转移(核心逻辑)
遍历 i
(s
的前 i
个字符)和 j
(p
的前 j
个字符),分两种情况:
情况 1:p[j-1] ≠ '*'
(普通字符或 .
)
- 若
s[i-1]
与p[j-1]
匹配(s[i-1] == p[j-1]
或p[j-1] == '.'
),且s
的前i-1
个字符与p
的前j-1
个字符匹配(dp[i-1][j-1] == true
),则dp[i][j] = true
。
情况 2:p[j-1] == '*'
(匹配前一个字符0次或多次)
*
修饰 p[j-2]
,分两种子情况:
- 子情况 1:
*
匹配0次:忽略p[j-2]
和p[j-1]
,则dp[i][j] = dp[i][j-2]
。 - 子情况 2:
*
匹配至少1次:需满足:s[i-1]
与p[j-2]
匹配(s[i-1] == p[j-2]
或p[j-2] == '.'
);s
的前i-1
个字符与p
的前j
个字符匹配(dp[i-1][j] == true
,因为*
可延续匹配)。
此时dp[i][j] |= dp[i-1][j]
(“或”操作,继承子情况1的结果)。
完整代码(Java)
class Solution {public boolean isMatch(String s, String p) {int m = s.length();int n = p.length();boolean[][] dp = new boolean[m + 1][n + 1];dp[0][0] = true; // 空字符串匹配空正则// 初始化:处理p中前导的*(空s匹配p的情况)for (int j = 2; j <= n; j++) {if (p.charAt(j - 1) == '*') {dp[0][j] = dp[0][j - 2];}}// 填充DP表for (int i = 0; i <= m; i++) {for (int j = 1; j <= n; j++) {if (p.charAt(j - 1) != '*') {// 情况1:普通字符或.,需i>0且当前字符匹配,且前i-1和j-1匹配if (i > 0 && (s.charAt(i - 1) == p.charAt(j - 1) || p.charAt(j - 1) == '.')) {dp[i][j] = dp[i - 1][j - 1];}} else {// 情况2:*,分匹配0次和至少1次// 子情况1:匹配0次,直接继承j-2的状态dp[i][j] = dp[i][j - 2];// 子情况2:匹配至少1次,需i>0且当前字符匹配,继承i-1的状态if (i > 0 && (s.charAt(i - 1) == p.charAt(j - 2) || p.charAt(j - 2) == '.')) {dp[i][j] |= dp[i - 1][j];}}}}return dp[m][n];}
}
关键细节解析
- 索引处理:
s[i-1]
和p[j-1]
对应字符串的第i
、j
个字符(数组从0开始)。 *
的灵活性:通过“匹配0次”和“匹配至少1次”的逻辑,覆盖所有可能的重复情况。- 初始化优化:提前处理
p
中前导*
的情况,确保空字符串的匹配性。
复杂度分析
- 时间复杂度:
O(m×n)
,m
和n
分别是s
和p
的长度,双重循环遍历所有状态。 - 空间复杂度:
O(m×n)
,存储二维 DP 表。
示例验证
- 示例 2(
s="aa", p="a*"
):dp[2][2]
:p[1]='*'
,匹配0次时dp[2][0]=false
;匹配至少1次时,s[1]='a' == p[0]='a'
,且dp[1][2]=true
(i=1
时已匹配),故dp[2][2]=true
。
- 示例 3(
s="ab", p=".*"
):p[1]='*'
修饰.
,匹配0次时dp[2][0]=false
;匹配至少1次时,s[1]='b'
与.
匹配,且dp[1][2]=true
(i=1
时s[0]='a'
匹配.
),故dp[2][2]=true
。
该方案通过动态规划精准建模匹配状态,高效处理 *
的复杂逻辑,确保了正则表达式匹配的正确性与性能。