给定一个字符串 (s) 和一个字符模式 (p) ,实现一个支持 '?' 和 '*' 的通配符匹配。
'?' 可以匹配任何单个字符。
'*' 可以匹配任意字符串(包括空字符串)。
两个字符串完全匹配才算匹配成功。
说明:
s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母,以及字符 ? 和 *。
刚开始想到的一个方法就是做递归,情况可以分为如下几种
- s空串且p空串,返回true
- s不为空但p为空,返回false
- s为空但p不为空,根据p做判断,如果p的第一个字符是'*',可以继续递归下去,否则返回false
- s不为空且p不为空,这个时候就要考虑多种情况了。
代码如下:
public boolean isMatch(String s, String p) {
if (s == null || p == null) {
return false;
}
if (p.equals("")) {
return s.equals("");
} else {
if (s.equals("")) {
return p.charAt(0) == '*' && isMatch(s, p.substring(1));
} else {
if (p.charAt(0) == '?') {
return isMatch(s.substring(1), p.substring(1));
} else if (p.charAt(0) == '*') {
// 空、一次或者多次
return isMatch(s, p.substring(1)) || isMatch(s.substring(1), p) || isMatch(s.substring(1), p.substring(1));
} else {
return s.charAt(0) == p.charAt(0) && isMatch(s.substring(1), p.substring(1));
}
}
}
}
最重要的是理解,当p的第一个字符是'*'的情况,可以匹配零次、一次或者多次。但是很不幸的是,上面的方法超时了,确实,本来这样递归就是很耗时间,因为很多重复的计算。
后来看了一下别人的一个动态规划方法,也跟着写了一下,代码如下:
public boolean isMatch(String s, String p) {
if (s == null || p == null) {
return false;
}
int lenS = s.length();
int lenP = p.length();
boolean[][] dp = new boolean[lenP+1][lenS+1];
dp[0][0] = true;
for (int i = 1;i <= lenP;++i) {
dp[i][0] = p.charAt(i-1) == '*' && dp[i-1][0];
}
for (int i = 1;i <= lenP;++i) {
for (int j = 1;j <= lenS;++j) {
if (p.charAt(i-1) == '?') {
dp[i][j] = dp[i-1][j-1];
} else if (p.charAt(i-1) == '*') {
dp[i][j] = dp[i-1][j] || dp[i-1][j-1] || dp[i][j-1]; // 零次、一次或多次
} else {
dp[i][j] = (s.charAt(j-1) == p.charAt(i-1)) && dp[i-1][j-1];
}
}
}
return dp[lenP][lenS];
}
重要的还是需要理解是如何匹配零次、一次或者多次。