目录
题目描述
算法
1、回溯法
算法描述
算法实现
2、DP(动态规划法)
算法描述
算法实现
Given an input string (s
) and a pattern (p
), implement regular expression matching with support for '.'
and '*'
.
'.' Matches any single character.
'*' Matches zero or more of the preceding element.
The matching should cover the entire input string (not partial).
Note:
s
could be empty and contains only lowercase letters a-z
.p
could be empty and contains only lowercase letters a-z
, and characters like .
or *
.Example 1:
Input:
s = "aa"
p = "a"
Output: false
Explanation: "a" does not match the entire string "aa".
Example 2:
Input:
s = "aa"
p = "a*"
Output: true
Explanation: '*' means zero or more of the precedeng element, 'a'. Therefore, by repeating 'a' once, it becomes "aa".
Example 3:
Input:
s = "ab"
p = ".*"
Output: true
Explanation: ".*" means "zero or more (*) of any character (.)".
Example 4:
Input:
s = "aab"
p = "c*a*b"
Output: true
Explanation: c can be repeated 0 times, a can be repeated 1 time. Therefore it matches "aab".
Example 5:
Input:
s = "mississippi"
p = "mis*is*p*."
Output: false
考虑到*可以重复前面字符任意次的特性,如果从前向后进行匹配我们很难确定*表示的重复次数,因此我们从后面往前面进行匹配。
对于简单的情况如果s[i]和p[j]都是字符,没有‘*’(可以有‘.’),我们可以简单的判断其是否可匹配
对于p[j]的字符是‘*’的情况,首先‘*’都是和其前面一个字符组合起作用的,具体执行思路如下
//回溯法
class Solution {
public:
bool isMatch(string s, string p) {
return isMatch(s,s.length() - 1, p, p.length() - 1);
}
bool isMatch(string s, int i, string p, int j)
{
//如果p已经匹配完,而s还没有则错误,如果二者同时匹配完则正确
if(j == -1)
{
if(i == -1)return true;
else
return false;
}
if(s[i] == p[j] || p[j] == '.')return isMatch(s,i-1,p,j-1);
if(p[j] == '*')
{
if((i > -1) && (s[i] == p[j - 1] || p[j-1] == '.'))
{
//这里要用j而不是j-2,因为#*可能匹配s的不止一个字符
if(isMatch(s,i-1,p,j))return true;
}
return isMatch(s,i,p,j-2);
}
return false;
}
};
与回溯法相反,动态规划法是我偏要从前向后扫描计算能否匹配,其定义了一个(n+1)*(m+1)的bool型数组,根据已有的位置数据将整个数组填满
a),初始化数组的头,以方便后续数值传递
b[0][0]值为true,因为空字符串可以匹配空字符串
b[*][0]值为false,因为空字符串p不能匹配非空s
b[0][*]值要判断其是否前面均为#+*的组合,若是则真,否则为假
b),数值传递
(i),对于p当前字符不为*的字符,非常简单,b[i][j] = b[i - 1][j - 1] && (p[j - 1] == '.' || p[j - 1] == s[i - 1]),即值由两字符串当前位置的字符值是否相等和两字符串在当前位置之前的字符能否匹配决定
(ii),对于p当前字符为*的字符,则较为复杂,其值由以下两种情况综合决定,无论一下哪种情况满足,其值为真;若均不满足,则其值为假
<1>*表示其前的字符重复了0次,即其值与b[i][j - 2]的值相关;
<2>*表示其前的字符重复了1次或几次,则需判断p当前位置的前一个位置的字符与s当前位置的字符能否匹配,且其值更与b[i - 1][j]相关
class Solution{
public:
bool isMatch(string s, string p) {
int m = s.length();
int n = p.length();
bool b[m+1][n+1];
//两个字符串都是空串的情况
b[0][0] = true;
//对于空字符串p来说,不能匹配任意非空字符串s
for(int i = 1; i <= m; i ++)b[i][0] = false;
//反之,对于空字符串s来说,有可能匹配非空字符串p,只要全为形如“#*”的组合即可
for(int j = 1; j <= n; j ++)b[0][j] = j > 1 && (p[j - 1] == '*') && b[0][j-2];
//编程时我在考虑整个二维数组的扫描顺序时有所犹豫,但是仔细推敲发现行行扫描和列列扫描并无影响
for(int i = 1; i <= m; i ++)
{
for(int j = 1; j <= n; j ++)
{
if(p[j - 1] == '*') b[i][j] = b[i][j - 2] || ((p[j - 2] == s[i - 1] || p[j - 2] == '.') && b[i - 1][j]);
else b[i][j] = b[i - 1][j - 1] && (p[j - 1] == '.' || p[j - 1] == s[i - 1]);
}
}
return b[m][n];
}
};