原题:
Implement wildcard pattern matching with support for '?'
and
'*'
.
'?' Matches any single character. '*' Matches any sequence of characters (including the empty sequence). The matching should cover the entire input string (not partial). The function prototype should be: bool isMatch(const char *s, const char *p) Some examples: isMatch("aa","a") → false isMatch("aa","aa") → true isMatch("aaa","aa") → false isMatch("aa", "*") → true isMatch("aa", "a*") → true isMatch("ab", "?*") → true isMatch("aab", "c*a*b") → false
即判断字符串s是否符合通配符p。p里面的'?'可以匹配任何单一字符,'*'可以匹配任意字符串。
思考过程:
一开始想起前面的正则表达式匹配,用递归和回溯的办法,遍历'*'匹配所有字符串的情况。属于暴力破解,代码很精简,当然超时了。后来有个思路:只要匹配出上p中所有
非'*'字符,多余字符所在位置有'*'就可以了。写了个函数,可惜还是超时了:
public boolean isMatchTLE(String s, String p) {
if (p.isEmpty()) return s.isEmpty();
if ((!s.isEmpty()) && (p.charAt(0) == s.charAt(0) || p.charAt(0) == '?')) return isMatchTLE(s.substring(1),p.substring(1));
if (p.charAt(0) == '*') { //return (isMatchTLE(s,p.substring(1)) || (!s.isEmpty() && isMatchTLE(s.substring(1),p)));
do {
if (p.charAt(0) == '?')
if (!s.isEmpty()) s = s.substring(1);
else return false;
if (p.length() == 1)
if (p.charAt(0) == '*') return true;
else return true;
p = p.substring(1);
} while (p.charAt(0) == '*' || p.charAt(0) == '?');
char c = p.charAt(0);
for (int i = 0;i < s.length();i++)
if (s.charAt(i) == c)
if (isMatchTLE(s.substring(i),p)) return true;
return false;
}
return false;
}
后来看到别人的方法,思路区别关键在于:如果当期匹配出了问题,找到最近出现'*'的地方去改变'*'匹配的字符个数就可以了,再往前面的不需要去尝试改了。我们还是本着把所有p中非'*'字符匹配完的原则就可以。能匹配到现在,说明前面非'*'字符串都匹配好了,出了问题去上次'*'位置就可以了,不用再考虑前面的'*'。
解题思路:
这里要注意p如果已经遍历完了,如果s还没遍历完,说明前面'*'匹配的字符串短了。比如s = "aaaa",p = "***a"这种情况,这就需要判断p的最后的'*'后面的尾缀是否和s尾缀匹配,重新从上次出现'*'的地方匹配s。其它见代码注释。
结果代码:
public boolean isMatch(String s,String p){
int latestStarS = 0,latestStarP = -1,i = 0,j = 0;//latestStarS和latestStarP分别用于标记上次出现‘*’时s和p遍历到的位置
while (i < s.length()){//对s入手进行遍历
if (j < p.length() && (s.charAt(i) == p.charAt(j) || p.charAt(j) == '?')) {//当前字母匹配成功,向后继续匹配
i++;j++;
continue;
}
if (j < p.length() && p.charAt(j) == '*'){//遇到'*'做标记,默认在s里先让'*'不匹配任何字符。
latestStarS = i;
latestStarP = j;
j++;
continue;
}
if (latestStarP != -1){//既没匹配又没遇到'*'的情况,如果之前出现过'*',返回那次的位置,'*'匹配s里比上次匹配字符串多一个字符的字符串;
i = ++latestStarS;// 还有p已经遍历完的情况,那么需要判断p的'*'后面的尾缀和s的尾缀是否相同。
j = latestStarP + 1;
}
else return false;//没匹配也没'*',之前还没出现过'*'
}
while (j < p.length()) {//如果p最后剩的全是'*',返回true,不全是,返回false
if (p.charAt(j) != '*') return false;
j++;
}
return true;
}