题目描述
给定一个字符串 (s
) 和一个字符模式 (p
)。实现支持 '.'
和 '*'
的正则表达式匹配。
'.' 匹配任意单个字符。
'*' 匹配零个或多个前面的元素。
匹配应该覆盖整个字符串 (s
) ,而不是部分字符串。
说明:
s
可能为空,且只包含从a-z
的小写字母。p
可能为空,且只包含从a-z
的小写字母,以及字符.
和*
。
示例 1:
输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。
示例 2:
输入:
s = "aa"
p = "a*"
输出: true
解释: '*' 代表可匹配零个或多个前面的元素, 即可以匹配 'a' 。因此, 重复 'a' 一次, 字符串可变为 "aa"。
示例 3:
输入:
s = "ab"
p = ".*"
输出: true
解释: ".*" 表示可匹配零个或多个('*')任意字符('.')。
示例 4:
输入:
s = "aab"
p = "c*a*b"
输出: true
解释: 'c' 可以不被重复, 'a' 可以被重复一次。因此可以匹配字符串 "aab"。
示例 5:
输入:
s = "mississippi"
p = "mis*is*p*."
输出: false
题解
DP
参考:Java DP solution beats %100 with explanation 👍
用 dp[i,j] 表示 s.Substring(0,i) 是否可以用 p.Substring(0,j) 匹配。例如 dp[0,0] = true,因为当 s 和 p 都是空时它们相匹配。
s='aab', p='c*a*b'
c * a * b
0 1 2 3 4 5
0 y
a 1
a 2
b 3
针对第一列 p = “” 只有字符串 s = “” 与它匹配,那就是 dp[0,0],这也意味着第一列除了 dp[0,0] 都是 false。
s='aab', p='c*a*b'
c * a * b
0 1 2 3 4 5
0 y
a 1 n
a 2 n
b 3 n
针对第一行 s = “”,p = “” 或 p =”a“(任意字符) 都能与它匹配,甚至 p = “abc*” 都能与其匹配。下面就是循环填充第一行的代码:
for (int j=2; j<=p.length(); j++)
{
dp[0][j] = p.charAt(j-1) == '*' && dp[0][j-2];
}
处理完第一行和第一列后我们的矩阵就变成了:
注意因为 p = “c“ 和 p=”ca*” 都能匹配空字符串,所以 dp[0,2] 和 dp[0,4] 是 true。
s='aab', p='c*a*b'
c * a * b
0 1 2 3 4 5
0 y n y n y n
a 1 n
a 2 n
b 3 n
现在我们可以开始主循环了。分两种情况:
p[j-1] == s[i-1] || p[j-1] == '.'
:如果当前字符相同或表达式是.
那匹配与否由之前的状态决定dp[i,j] = dp[i-1,j-1]
。之所以索引都是 i-1,j-1 有 -1 的偏移,是因为我们的 dp 数组尺寸比字符串大 1,以保存初始状态dp[0,0]
如果
p[j-1] == '*'
那么可以将它视为一个空集,dp[i,j]
等于dp[i,j-2]
或(s[i-1] p[j-2] || p[j-2] == '.')
。又因为当前的字符需要与表达式 * 之前的字符相匹配,所以还需要与dp[i-1,j]
于是最终的 dp 矩阵如下:
s='aab', p='c*a*b'
c * a * b
0 1 2 3 4 5
0 y n y n y n
a 1 n n n y y n
a 2 n n n n y n
b 3 n n n n n y
DP 代码:
public bool IsMatch(string s, string p)
{
if (string.IsNullOrEmpty(p)) return string.IsNullOrEmpty(s);
var dp = new bool[s.Length + 1, p.Length + 1];
dp[0, 0] = true;
for (var j = 2; j <= p.Length; j++)
{
dp[0, j] = p[j - 1] == '*' && dp[0, j - 2];
}
for (var j = 1; j <= p.Length; j++)
{
for (var i = 1; i <= s.Length; i++)
{
if (p[j - 1] == s[i - 1] || p[j - 1] == '.')
{
dp[i, j] = dp[i - 1, j - 1];
}
else if (p[j - 1] == '*')
{
dp[i, j] = dp[i, j - 2] || ((s[i - 1] == p[j - 2] || p[j - 2] == '.') && dp[i - 1, j]);
}
}
}
return dp[s.Length, p.Length];
}
时间和空间复杂度都是 O(p.Length * s.Length)。