题目
给定一个字符串 (s) 和一个字符模式 § ,实现一个支持 ‘?’ 和 ‘*’ 的通配符匹配。
‘?’ 可以匹配任何单个字符。
‘*’ 可以匹配任意字符串(包括空字符串)。
两个字符串完全匹配才算匹配成功。
说明:
s 可能为空,且只包含从 a-z 的小写字母。 p 可能为空,且只包含从 a-z 的小写字母,以及字符 ? 和 *。
示例 1:
输入:
s = “aa”
p = “a”
输出: false
解释: “a” 无法匹配 “aa” 整个字符串。
示例 2:
输入:
s = “aa”
p = ""
输出: true
解释: '’ 可以匹配任意字符串。
示例 3:
输入:
s = “cb”
p = “?a”
输出: false
解释: ‘?’ 可以匹配 ‘c’, 但第二个 ‘a’ 无法匹配 ‘b’。
示例 4:
输入: s = “adceb”
p = “ab”
输出: true
解释: 第一个 ‘’ 可以匹配空字符串, 第二个 '’
可以匹配字符串 “dce”.
示例 5:
输入:
s = “acdcb”
p = “a*c?b”
输入: false
我的答案
思路
(1)如果p或者s为空,很好判断1
(2)如果p的第一位不为 ’ * ',则当且仅当首字母匹配且F(s-1,p-1)匹配 才true
(3)如果p的第一位为 ’ * ',则分以下几种情况
1、p中只剩一个 ’ * ’ 了,则肯定true
2、找到p中的下一个非’ * ’ 符号,如果是 ’ ? ’ 则继续找,然后在s中找到它的位置。若F(s-k,p-next)为true,说明s的后面匹配p的后面部分,则返回true
代码如下
public static boolean isMatch(String s, String p) {
//如果p为空,当且仅当s为空
if(p.isEmpty())
return s.isEmpty();
//如果s为空,当且仅当p全为***
if(s.isEmpty()) {
int index = 0;
while(p.charAt(index++) == '*') {
//如果找完了都还是*,那么p全部为*****,一定true
if(index==p.length()) return true;
}
return false;
}
if(p.replace("*", "").length()>s.length())
return false;
//若p第一个不为*,则当且仅当首字母匹配,且F(s-1,p-1)匹配
if(p.charAt(0)!='*') {
return (p.charAt(0)=='?'||s.charAt(0)==p.charAt(0))&&isMatch(s.substring(1),p.substring(1));
}
//若p第一位为*,则分为以下几种情况
if(p.charAt(0)=='*') {
//若p只含有这个*了,则true
if(p.length()==1)
return true;
//找到P的下一个非*字符,序号为next
int next = 0;
int numX = 0;//记录?的个数
while(p.charAt(next)=='*'||p.charAt(next)=='?') {
if(p.charAt(next)=='?') {//如果是问号的话
//System.out.println("找到一个问号,位置"+next);
numX++;
next++;
if(numX>s.length()) return false;//?个数比s的长度还大
if(next==p.length()) return true;//长度检测
continue;
}
next++;
if(next==p.length()) return true;//长度检测
}
//从s中找这个字符,其序号为k
int k = s.indexOf(p.charAt(next));
//System.out.println("s:"+s+", p:"+p+", next:"+next+", k:"+k+" ,numX:"+numX);
//如果s中找不到,则不匹配
if(k == -1) return false;
//若问号的个数比s中找到的第一个匹配字符还大的话,该匹配字符不可用!
if(numX>k) {
System.out.println("numX>k,匹配s="+s.substring(numX)+", p="+('*'+p.substring(next)));
return isMatch(s.substring(numX), '*'+p.substring(next));
}
//若s-k和p-next匹配了,就true
if(isMatch(s.substring(k), p.substring(next)))
return true;
else if(k ==0) {
return isMatch(s.substring(1), p);
}else
return isMatch(s.substring(k), p);
}
return true;
}
动态规划
思路解析
使用 boolean[][] m
来表示s(0 ~ i-1)和 p(0 ~ j-1)是否匹配
对m[i][j]
而言
若p(j-1) == ' * '
,则当且仅当 当前字符匹配s(i-1)==p(j-1)
且 m[s-1,p-1]
为true
时才匹配
若p(j-1) != ' * '
, 则当m[i][j-1]
为true
或者 m[i-1][j]
为true
的时候 匹配
其中:
m[i][j-1] 即当前’‘匹配一个空字符
m[i-1][j] 即当前’'匹配一个字符s.charAt(i-1)
**注意!!!**动态规划中用的自底向上的算法,与普通迭代算法自顶向下的迭代思路会有不同,一般动态规划更加简洁清晰!
代码如下
//别人的动态规划解法
//m[i][j]表示s(0 ~ i-1)和 p(0 ~ j-1)是否匹配
public static boolean isMatch3(String s, String p) {
boolean[][] m = new boolean[s.length()+1][p.length()+1];
m[0][0] = true; //两个字符串都为空是肯定匹配
for(int i = 0; i <= s.length(); i++) {
for(int j = 1; j <= p.length(); j++) {
if(p.charAt(j-1) == '*') {
//m[i][j-1] 即当前'*'匹配一个空字符
//m[i-1][j] 即当前'*'匹配一个字符s.charAt(i-1)
//这里之所以不是m[i-1][j-1]是因为当前'*'可能在前面一匹配了若干个字符
m[i][j] = m[i][j-1] || (i > 0 && m[i-1][j]);
} else {
//前面的m(0~i-2)和p(0~j-2)能匹配且当前字符能匹配
m[i][j] = i > 0 && m[i-1][j-1] && (s.charAt(i-1) == p.charAt(j-1) || p.charAt(j-1) == '?');
}
}
}
return m[s.length()][p.length()];
}