描述
题目描述
请实现一个函数用来匹配包括'.'和''的正则表达式。模式中的字符'.'表示任意一个字符,而''表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但是与"aa.a"和"ab*a"均不匹配
示例
输入:"aaa","a*a" 返回值:true
引言
知识点:动态规划,递归,正则表达式
难度:⭐⭐⭐⭐
题解
解题思路
对于动态规划解法,总的思路便是状态转移,即不断判断从 str[:1] 和 pattern[:1] ,即从第一个字符开始判断是否匹配,直到整个字符串完全匹配。
如下图的图解,每次状态转移总共有两种状态:(1)str 增加一个字符,判断是否匹配(2)pattern增加一个字符,判断是否匹配
方法一:动态规划
- 每个字符有三种情况出现:“字符”、”.“、“*”
- 状态定义:
dp[i][j]表示字符串 str的前 i 个字符串和 pattern 的前 j 个字符串是否匹配 - 状态转移:主要是对 “*” 的状态处理
- 当
pattern[i - 1] != “*”,即上一位不为*。dp[i][j]在以下任何一种情况为true时,则等于true- 1、
dp[i - 1][j - 1] && str[i - 1] == pattern[j - 1],即上一个状态和当前位的字符都匹配 - 2、
dp[i - 1][j - 1] && pattern[j - 1] == '.',即上一个状态为true,且pattern上一位包含 ‘.’
- 1、
- 当
pattern[i - 1] == “*”,即当前位的上一位为*。dp[i][j]在以下任何一种情况为true时,则等于true- 1、
dp[i][j - 2],即 j-2 位 的pattern能满足,则 i-1 位是‘*’作为任意数量的值必定能满足匹配 - 2、
dp[i - 1][j]&&str[i - 1] == pattern[j - 2];即让字符 pattern[j-2]多出现几次,看能否匹配 - 3、
dp[i - 1][j] && pattern[j - 2] == '.', 即让字符'.'多出现 1 次时,能否匹配;
- 1、
- 当
- 状态初始化
dp[0][0] == true: 代表两个空字符串能够匹配。dp[0][j] = dp[0][j - 2]且p[j - 1] = '*', 即当前pattern的0到j位是true还是false,取决于dp[0][j-2]是否匹配,以及 pattern的当前位的上一位是否为‘*’,因为‘*’可以匹配任何值,包括空值
- 返回值
dp矩阵右下角字符,代表字符串str和pattern能否匹配。
图解


算法流程:
- 定义状态,并初始化
- 遍历每个str和pattern中的字符
- 1、当比较的位pattern[j-1]=='.', 或者字符相等匹配,则状态取决于上一次状态
- 2、对于包含 * 的匹配
- 当之前位为 '.', 或者字符相等,则匹配
- 否则只能不匹配
- 返回状态转移最后的结果
Java 版本代码如下:
import java.util.*;
public class Solution {
public boolean match (String str, String pattern) {
int strLen = str.length() + 1, patternLen = pattern.length() + 1;
// 定义状态,并初始化
boolean[][] dp = new boolean[strLen][patternLen];
dp[0][0] = true;
// 初始化首行
for(int j = 2; j < patternLen; j++) {
// 因为pattern[j-1]的 * 可以取任意值包括空值,因此dp[0][j]相当于取决于dp[0][j-2]
if(pattern.charAt(j - 1) == '*') {
dp[0][j] = dp[0][j - 2];
}
}
// 状态转移
for(int i = 1; i < strLen; i++) {
for(int j = 1; j < patternLen; j++) {
// 当比较的位pattern[j-1]=='.', 或者字符相等匹配,则状态取决于上一次状态
if (pattern.charAt(j - 1) == '.' || str.charAt(i - 1) == pattern.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1];
} else if (j >= 2 && pattern.charAt(j - 1) == '*'){
// 对于包含 * 的匹配
// 当之前位为 '.', 或者字符相等,则匹配
if (pattern.charAt(j - 2) == '.' || pattern.charAt(j - 2) == str.charAt(i - 1)) {
dp[i][j] = dp[i][j - 2] || dp[i - 1][j - 2] || dp[i - 1][j];
} else {
// 否则只能不匹配
dp[i][j] = dp[i][j - 2];
}
}
}
}
return dp[strLen - 1][patternLen - 1];
}
} Python 版本代码如下:
class Solution:
def match(self, s, p):
if not s and not p:
return True
m, n = len(s) + 1, len(p) + 1 # 两个字符串的长度
dp = [[False] * n for i in range(m)]
# 初始化
dp[0][0] = True
for j in range(2, n, 2):
dp[0][j] = dp[0][j - 2] and p[j - 1] == '*'
# 状态转移
for i in range(1, m):
for j in range(1, n):
if p[j - 1] == '*':
if dp[i][j - 2]: dp[i][j] = True
elif dp[i - 1][j] and s[i - 1] == p[j - 2]: dp[i][j] = True
elif dp[i - 1][j] and p[j - 2] == '.': dp[i][j] = True
else:
if dp[i - 1][j - 1] and s[i - 1] == p[j - 1]: dp[i][j] = True
elif dp[i - 1][j - 1] and p[j - 1] == '.': dp[i][j] = True
return dp[-1][-1]
复杂度分析:
时间复杂度 O(SP):其中 S,P 分别为 str 和 pattern 的长度,状态转移需遍历整个 dp 矩阵
空间复杂度 O(SP):状态二维矩阵 dp

京公网安备 11010502036488号