问题分析
这道题的难点主要在于*
号的匹配,这里记dp[i][j]
表示s[1...i]
和p[1...j]
能否完成匹配,先根据特殊情况归纳总结:
*
号匹配 0 次,则dp[i][j] = dp[i][j-2]
*
号匹配 1 次,则dp[i][j] = dp[i-1][j-2] && s[i] == p[j-1]
*
号匹配 2 次,则dp[i][j] = dp[i-2][j-2] && s[i-1] == p[j-1] && s[i] == p[j-1]
*
号匹配 3 次,则dp[i][j] = dp[i-3][j-2] && ...
*
号匹配 k 次,则dp[i][j] = dp[i-k][j-2] && ...
对上述进行归纳可得:
- 当
k = 0
时,dp[i][j] = dp[i][j-2]
- 当
k >= 1
时,将dp[i][j] = dp[i][j-2]
带入等式左边,可得dp[i][j-2] = dp[i-k][j-2] && ...
。因此,最终化简可得dp[i][j] = dp[i-1][j] && s[i] == p[j-1]
算法描述
状态定义:dp[i][j]
表示s[1...i]
和p[1...j]
能否完成匹配
状态转移:
若p[j] == '*'
,可能的状态转移如下:
*
表示匹配 0 个前面的那一个元素:dp[i][j] = dp[i][j-2]
*
表示匹配多个前面的那一个元素:dp[i][j] = (s[i] == p[j-1] || p[j-1] == '.') && dp[i-1][j]
若p[j] != '*'
,则只能进行单元素的匹配:dp[i][j] = (s[i] == p[j] || p[j] == '.') && dp[i-1][j-1]
边界情况:
dp[0][0] = true
- 子字符规律
p[1...i]
可能匹配空字符串s
:dp[0][i] = p[i] == '*' && dp[0][i-2]
程序代码
C++
class Solution {
public:
bool isMatch(string s, string p) {
int m = s.size(), n = p.size();
s = " " + s;
p = " " + p;
vector<vector<bool>> dp(m + 1, vector<bool>(n + 1, false));
// 初始化
dp[0][0] = true;
for(int i = 2; i <= n; i++) dp[0][i] = p[i] == '*' && dp[0][i-2];
for(int i = 1; i <= m; i++) {
for(int j = 1; j <= n; j++) {
if(p[j] == '*') {
dp[i][j] = dp[i][j-2] || (s[i] == p[j-1] || p[j-1] == '.') && dp[i-1][j];
}
else {
dp[i][j] = (s[i] == p[j] || p[j] == '.') && dp[i-1][j-1];
}
}
}
return dp[m][n];
}
};
Go
func isMatch(s string, p string) bool {
m, n := len(s), len(p)
s = " " + s
p = " " + p
dp := make([][]bool, m + 1)
for i := 0; i < len(dp); i++ {
dp[i] = make([]bool, n + 1)
}
// 初始化
dp[0][0] = true
for i := 2; i <= n; i++ {
dp[0][i] = dp[0][i-2] && p[i] == '*'
}
for i := 1; i <= m; i++ {
for j := 1; j <= n; j++ {
if p[j] == '*' {
dp[i][j] = dp[i][j-2] || dp[i-1][j] && (s[i] == p[j-1] || p[j-1] == '.')
} else {
dp[i][j] = dp[i-1][j-1] && (s[i] == p[j] || p[j] == '.')
}
}
}
return dp[m][n]
}