题目链接
通配符匹配
题目描述
注意点
- s 仅由小写英文字母组成
- p 仅由小写英文字母、‘?’ 或 ‘*’ 组成
- ‘?’ 可以匹配任何单个字符
- ‘*’ 可以匹配任意字符序列(包括空字符序列)
解答思路
- 最初想到的是dfs + 剪枝,但是用例超时了
- 参照题解使用动态规划解决本题,其思路为:创建一个大小dp[pLen + 1][sLen + 1]的二维数组,其中i行表示p的第(i - 1)位,j列表示s的第(j- 1)位,dp[i][j]表示p的前(i - 1)位和s的前(j - 1)位是否能匹配,从p的第1个字符开始遍历,判断其是否能和s的前(j - 1)位匹配,如果匹配则置dp[i - 1][j - 1]为true,当处于dp[i][j],有以下几种情况:
(1)如果p对应i位置的字符为*,则该位置处的字符一定相等,dp[i][j]取决于dp[i][j - 1] || dp[i - 1][j - 1]
(2)如果p对应i位置的字符为?,则该位置处的字符一定相等,但是不能视为空,dp[i][j]取决于dp[i - 1][j] || dp[i][j - 1] || dp[i - 1][j - 1]
(3)如果p对应i位置和s对应j位置字符相同,则dp[i][j]也取决于dp[i - 1][j] || dp[i][j - 1] || dp[i - 1][j - 1]
(4)如果p对应i位置和s对应j位置字符不相同,则dp[i][j]为false - 注意p的前n位都为’*'的情况要特殊考虑,由于其可以表示为空字符串,所以可以视为dp[0~n][0]都为true
代码
class Solution {
public boolean isMatch(String s, String p) {
int sLen = s.length();
int pLen = p.length();
boolean[][] dp = new boolean[pLen + 1][sLen + 1];
dp[0][0] = true;
for (int i = 1; i <= pLen; i++) {
boolean isSame = false;
for (int j = 0; j <= sLen; j++) {
// 如果p的前i为都是'*',则dp[i][0]为true('*'可以表示为空字符串所以可以忽略)
if (p.charAt(i - 1) == '*' && j == 0 && dp[i - 1][j]) {
dp[i][0] = true;
isSame = true;
continue;
}
if (j == 0) {
continue;
}
// '*'可以替代为空或任何字符(包括空字符串)
if (p.charAt(i - 1) == '*' && (dp[i - 1][j] || dp[i][j - 1] || dp[i - 1][j - 1])) {
dp[i][j] = true;
isSame = true;
continue;
}
// 该位置前面的子字符串不匹配
if (!dp[i - 1][j - 1]) {
continue;
}
// '?'可以匹配任意字符(空字符串除外)
if (p.charAt(i - 1) == '?' || p.charAt(i - 1) == s.charAt(j - 1)) {
dp[i][j] = true;
isSame = true;
}
}
// p的前i位和s无论如何都无法匹配,则可以直接认为s和p无法匹配
if (!isSame) {
break;
}
}
return dp[pLen][sLen];
}
}
关键点
- 注意p的前n位都为’*'的情况要特殊考虑,由于其可以表示为空字符串,所以可以视为dp[0~n][0]都为true,方便p.charAt(n + 1)与s.charAt(0)进行比较
- 如果某一行的dp值都为false,说明p的前i位和s无论如何都无法匹配,则可以直接认为s和p无法匹配,进行剪枝可以节省时间