Nameless Site

But one day, you will stand before its decrepit gate,without really knowing why.

0%

正则表达式匹配

来源Leetcode第10题正则表达式匹配

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持’.’和’*‘的正则表达式匹配。
‘.’匹配任意单个字符
‘*‘匹配零个或多个前面的那一个元素
所谓匹配,是要 涵盖整个字符串s的,而 不是部分字符串s

说明:
s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母,以及字符 ‘.’和’*‘。
示例 4:

输入:
s = “aab”
p = “c*a*b”
输出: true
解释: 因为’*‘表示零个或多个,这里’c’为 0 个, ‘a’ 被重复一次。因此可以匹配字符串 “aab”。

回溯

采用递归的思想,当前层匹配了就可以匹配下一层。当遇到字符’.’时可以认为直接匹配进入下一层,当遇到字符’*‘时要考虑字符’*‘前一个字符出现的次数是0次还是多次,所以在递归的时候有两个方向的递归,一个是认为前一个字符出现0次,所以直接从模式串p的第3个字符开始匹配,一个是认为出现多次,因而需要模式串第一个字符与匹配串第一个字符匹配,在来匹配匹配串后续字符。
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public boolean isMatch(String text, String pattern) {
if (pattern.isEmpty()) return text.isEmpty(); //对应空串
boolean first_match = (!text.isEmpty() &&
(pattern.charAt(0) == text.charAt(0) || pattern.charAt(0) == '.')); //非空且第一个字符对应相等,或者p的一个字符为‘.’

if (pattern.length() >= 2 && pattern.charAt(1) == '*'){ //p的长度为2 且第二个字符为'*'的话
return (isMatch(text, pattern.substring(2)) || //看P第三个字符是否与s的字符匹配,可以参考例4,s=aab,p=c*a*b,因为*表示0个或多个,这里S有0个c,a被重复一次,所以是可以匹配的
(first_match && isMatch(text.substring(1), pattern))); //或者是第一个字符匹配,查看s的后续字符是否重复
} else {
return first_match && isMatch(text.substring(1), pattern.substring(1)); //进行下一个字符的匹配
}
}
}

动态规划

可以看到回溯的话时空复杂度相当的高,因为题目拥有最优子结构,一个自然的想法是将中间结果保存起来。我们通过用dp[i,j]表示text[i:]和pattern[j:]是否能匹配,即用户dp[i][j]表示s的前i个能否被p的前j的匹配。
考虑转移方程:
1.p[j] == s[i] -> dp[i][j] = dp[i-1][j-1]
2.if p[j] == ‘.‘ -> dp[i][j] = dp[i-1][j-1]
3.if p[j] = ‘*‘ ->
if p[j-1] != s[i] -> dp[i][j] = dp[i][j-2] //对应示例4,’*‘前的字符出现了0次
if p[j-1] == s[i] || p[j-1] == ‘.‘ -> dp[i][j] = dp[i-1][j] || dp[i][j] = dp[i][j-1] || dp[i][j] = dp[i][j-2] //分别对应着若干个字符匹配、单个字符匹配、没有字符匹配
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public boolean isMatch(String s, String p) {
int ls = s.length(), lp = p.length();
boolean[][] dp = new boolean[ls + 1][lp + 1];
dp[0][0] = true;
for(int j = 2; j <= lp; j++)
dp[0][j] = dp[0][j - 2] && p.charAt(j - 1) == '*'; //初始化第一列
for(int i = 1; i <= ls; i++) {
for(int j = 1; j <= lp; j++) {
if(p.charAt(j-1) == '*')
dp[i][j] = dp[i][j - 2] || dp[i - 1][j] &&
(s.charAt(i-1) == p.charAt(j - 2) || p.charAt(j - 2) == '.');
else if(s.charAt(i-1) == p.charAt(j-1) || p.charAt(j-1) == '.')
dp[i][j] = dp[i - 1][j - 1];
}
}
return dp[ls][lp];
}
}