10. 正则表达式匹配
题目描述
给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。
- ‘.’ 匹配任意单个字符
- ‘*’ 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。
示例1
输入:s = "aa" p = "a"
输出:false
解释:"a" 无法匹配 "aa" 整个字符串。
示例2
输入:s = "aa" p = "a*"
输出:true
解释:因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。
示例3
输入:s = "ab" p = ".*"
输出:true
解释:".*" 表示可匹配零个或多个('*')任意字符('.')。
示例4
输入:s = "aab" p = "c*a*b"
输出:true
解释:因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。
示例5
输入:s = "mississippi" p = "mis*is*p*."
输出:false
提示:
-
0 ≤ s . l e n g t h ≤ 20 0 \le s.length \le 20 0≤s.length≤20
-
0 ≤ p . l e n g t h ≤ 30 0 \le p.length \le 30 0≤p.length≤30
-
s 可能为空,且只包含从 a-z 的小写字母。
-
p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。
-
保证每次出现字符 * 时,前面都匹配到有效的字符
题解:
动态规划。
用 f [ i ] [ j ] f[i][j] f[i][j] 表示 s [ 0.. i ) s[0..i) s[0..i) 与 p [ 0... j ) p[0...j) p[0...j) 是否匹配,状态转移如下:
-
若 p[j - 1] != ‘*’:
- 若 p[j - 1] == ‘.’,万能匹配符, f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] f[i][j] = f[i - 1][j - 1] f[i][j]=f[i−1][j−1]
- 若 p[j - 1] == s[i - 1] ,同样的, f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] f[i][j] = f[i - 1][j - 1] f[i][j]=f[i−1][j−1]
-
若 p[j - 1] == ‘*’:
这种情况下,需要根据 ‘*’ 前面的字符重复几次来判断
-
重复 0 次,这种情况分为两种:
- 一是 p[j - 2] 与 s[i - 1] 根本不匹配,那么 p[j - 2] 和 p[j - 1] 可以忽略不计, f [ i ] [ j ] = f [ i ] [ j − 2 ] f[i][j] = f[i][j - 2] f[i][j]=f[i][j−2]
- 二是 p[j - 2] 与 s[i - 1] 匹配,但是不想让 p[j - 2] 和 s[i - 1] 匹配,那么 f [ i ] [ j ] = f [ i ] [ j − 2 ] f[i][j] = f[i][j - 2] f[i][j]=f[i][j−2]
-
重复多次,如果我们考虑重复多少次的话,这样的话,我们需要枚举次数:
f [ i ] [ j ] = f [ i − 1 ] [ j − 2 ] ( s [ i − 1 ] = p [ j − 2 ] ) f[i][j] = f[i - 1][j - 2](s[i - 1] = p[j - 2]) f[i][j]=f[i−1][j−2](s[i−1]=p[j−2])
f [ i ] [ j ] = f [ i − 2 ] [ j − 2 ] ( s [ i − 2 ] = s [ i − 1 ] = p [ j − 2 ] ) f[i][j] = f[i - 2][j - 2](s[i - 2] = s[i - 1] = p[j - 2]) f[i][j]=f[i−2][j−2](s[i−2]=s[i−1]=p[j−2])
…
f [ i ] [ j ] = f [ i − k ] [ j − 2 ] ( s [ i − k ] = s [ i − k + 1 ] = . . . = s [ i − 1 ] = p [ j − 2 ] f[i][j] = f[i - k][j - 2](s[i - k] = s[i - k + 1] = ... = s[i - 1] = p[j - 2] f[i][j]=f[i−k][j−2](s[i−k]=s[i−k+1]=...=s[i−1]=p[j−2])
这样会增加一层枚举的时间复杂度。。。
我们可以换个方向考虑,假设已经匹配 s[i - 1] ,将 s[i - 1] 扔掉,用 ‘p[j - 2]*’ 这个组合继续匹配下去,那么: f [ i ] [ j ] = f [ i − 1 ] [ j ] f[i][j] = f[i - 1][j] f[i][j]=f[i−1][j] ,而在求 f [ i − 1 ] [ j ] f[i - 1][j] f[i−1][j] 的过程中包含了上述枚举的状态,不会漏掉某个状态。
-
不管怎么考虑,时间复杂度是: O ( n ∗ m ) O(n*m) O(n∗m),而空间复杂度需要看具体写法。
代码一:
空间复杂度: O ( n ∗ m ) O(n * m) O(n∗m)
class Solution {
public:
bool isMatch(string s, string p) {
int lens = s.length();
int lenp = p.length();
if ( !lens && !lenp ) return true;
vector<vector<bool>> f( lens + 1, vector<bool>( lenp + 1, false ) );
f[0][0] = true;
for ( int i = 0; i <= lens; ++i ) {
for ( int j = 1; j <= lenp; ++j ) {
if ( p[j - 1] != '*' ) {
if ( i && (s[i - 1] == p[j - 1] || p[j - 1] == '.') )
f[i][j] = f[i - 1][j - 1];
} else {
if ( j > 1 && f[i][j - 2] ) f[i][j] = true;
else if ( i && j > 1 && ( p[j - 2] == '.' || s[i - 1] == p[j - 2] ) )
f[i][j] = f[i - 1][j];
}
}
}
return f[lens][lenp];
}
};
/*
时间:12ms,击败:59.53%
内存:7.2MB,击败:50.44%
*/
代码二:
空间复杂度: O ( m ) O(m) O(m),使用滚动数组优化。
class Solution {
public:
bool isMatch(string s, string p) {
int lens = s.length();
int lenp = p.length();
if ( !lens && !lenp ) return true;
vector<bool> f(lenp + 1, false), t;
f[0] = true;
for ( int i = 0; i <= lens; ++i ) {
if ( i ) fill( f.begin(), f.end(), false );
for ( int j = 1; j <= lenp; ++j ) {
if (p[j - 1] == '*' ) {
if ( j > 1 && f[j - 2] ) f[j] = true;
else if ( i && j > 1 && ( p[j - 2] == '.' || s[i - 1] == p[j - 2] ) )
f[j] = t[j];
} else {
if ( i && (s[i - 1] == p[j - 1] || p[j - 1] == '.') )
f[j] = t[j - 1];
}
}
t = f;
}
return f[lenp];
}
};
/*
时间:12ms,击败:59.53%
内存:6.5MB,击败:92.34%
*/