我正在研究如何在BST中找到最接近目标的k值,并且通过规则遇到以下实现:
'?'匹配任何单个字符。
'*'匹配任何字符序列(包括空序列)。
匹配应覆盖整个输入字符串(非部分)。
函数原型应该是: bool isMatch(const char * s,const char * p)
一些例子:
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
代码:
import java.util.*;
public class WildcardMatching {
boolean isMatch(String s, String p) {
int i=0, j=0;
int ii=-1, jj=-1;
while(i<s.length()) {
if(j<p.length() && p.charAt(j)=='*') {
ii=i;
jj=j;
j++;
} else if(j<p.length() &&
(s.charAt(i) == p.charAt(j) ||
p.charAt(j) == '?')) {
i++;
j++;
} else {
if(jj==-1) return false;
j=jj;
i=ii+1;
}
}
while(j<p.length() && p.charAt(j)=='*') j++;
return j==p.length();
}
public static void main(String args[]) {
String s = "aab";
String p = "a*";
WildcardMatching wcm = new WildcardMatching();
System.out.println(wcm.isMatch(s, p));
}
}
我的问题是,有两个额外索引ii
和jj
的原因是什么,为什么它们会被-1
初始化?每个人的目的是什么?不会使用i
和j
遍历它吗?
第一个if案例中ii=i;
和jj=j;
的目的是什么,第三个案例中i=ii+1;
和j=jj;
是什么?
最后,在什么情况下你会遇到while(j<p.length() && p.charAt(j)=='*') j++;
?
示例对理解非常有帮助。 提前感谢您,并接受回答/投票。
答案 0 :(得分:0)
看起来ii
和jj
用于处理与任何序列匹配的通配符“*”。它们的初始化为-1作为一个标志:它告诉我们是否已达到不匹配的序列并且当前没有评估“*”。我们可以逐个浏览您的示例。
请注意,i
与参数s
(原始字符串)相关,j
与参数p
(模式)相关。
isMatch("aa","a")
:
这会返回false,因为j<p.length()
语句在我们离开while循环之前会失败,因为p
(“a”)的长度只有1而s
的长度(“aa” )是2,所以我们将跳转到else块。这是-1初始化的地方:因为我们从未在p
中看到任何通配符,jj
仍为-1,表示字符串无法匹配,因此我们返回false。
isMatch("aa","aa")
:
s
和p
完全相同,因此程序重复评估else-if块没有问题,最后在i
等于2(长度为“ AA“)。第二个while循环从不运行,因为j
不小于p.length()
- 实际上,因为else-if一起递增i
和j
,它们都等于2和2不小于“aa”的长度。我们返回j == p.length()
,其评估结果为2 == 2
,并获得true
。
isMatch("aaa","aa")
:这个失败的原因与第一个相同。也就是说,字符串的长度不一样,我们从不打过通配符。
isMatch("aa","*")
:这就是它变得有趣的地方。首先我们将输入if块,因为我们在p
中看到了“*”。我们将ii
和jj
设置为0并仅增加j
。在第二次迭代中,j<p.length()
失败,因此我们跳转到else块。 jj
不再为-1(它为0),因此我们将j
重置为0并将i
设置为0 + 1。这基本上允许我们继续评估通配符,因为j
只是重置为jj
,它保存通配符的位置,ii
告诉我们从原始字符串开始的位置。此测试用例还解释了第二个while循环。在某些情况下,我们的模式可能比原始字符串短得多,因此我们需要确保它与通配符匹配。例如,isMatch("aaaaaa","a**")
应返回true,但最终的return语句正在检查是否j == p.length()
,询问我们是否检查了整个模式。通常我们会停在第一个通配符,因为它匹配任何东西,所以我们需要最终遍历模式的其余部分,并确保它只包含通配符。
从这里你可以找出其他测试用例背后的逻辑。我希望这有帮助!
答案 1 :(得分:0)
让我们看一下这个故障。
首先,这是字符串(s
)和通配符模式(p
)的并行迭代,使用变量i
来索引s
和变量{{ 1}}索引j
。
p
循环将在到达while
结束时停止迭代。当发生这种情况时,希望已经达到s
的结尾,在这种情况下,它会返回p
(true
)。
但是,如果j==p.length()
以p
结尾,那也是有效的(例如*
),以及isMatch("ab", "ab*")
循环确保的内容,即任何此时模式中的while(j<p.length() && p.charAt(j)=='*') j++;
被跳过,如果到达*
的末尾,则返回p
。如果未到达true
的结尾,则返回false。
这是你上一个问题的答案。现在让我们来看看循环。只要存在匹配项,p
将迭代else if
和i
,例如j
或'a' == 'a'
。
当找到'a' == '?'
通配符时(第一个*
),它会保存if
和ii
中的当前位置,以防需要回溯,然后跳过通配符字符。
这基本上首先假设通配符匹配空字符串(例如jj
)。当它继续迭代时,isMatch("ab", "a*b")
将匹配其余部分,方法最终返回else if
。
现在,如果找到不匹配(true
块),它将尝试回溯。当然,如果它没有保存的通配符(else
),它就不能回溯,所以它只返回jj==-1
。这就是false
初始化为jj
的原因,因此它可以检测是否保存了通配符。 -1
可以初始化为任何内容,但会初始化为ii
以保持一致性。
如果在-1
和ii
中保存了通配符位置,它将恢复这些值,然后将jj
转发一个,即假设下一个字符与通配符匹配,匹配的其余部分将成功并返回i
。
这就是逻辑。现在,它可以进行一点点优化,因为回溯是次优的。它目前将true
重置为j
,将*
重置为下一个字符。当它循环时,它将进入i
并再次在if
中保存保存值并将jj
值保存在i
中,然后递增ii
。由于这是给定的(除非达到j
的结束),回溯也可以这样做,保存迭代循环,即
s
答案 2 :(得分:0)
代码看起来很麻烦。 (见下文)
ii
和jj
的表面目的是实现一种回溯形式。
例如,当您尝试匹配&#34; abcde&#34;对于模式&#34; a * e&#34;,算法将首先匹配&#34; a&#34;在反对&#34; a&#34;的模式中在输入字符串中。然后它将急切地匹配&#34; *&#34;反对其余的字符串...并发现它犯了一个错误。此时,它需要回溯并尝试替代
ii
和jj
用于记录要回溯的点,并且使用这些变量要么记录新的回溯点,要么回溯。
或者至少,这可能是作者在某些方面的意图。
while(j<p.length() && p.charAt(j)=='*') j++;
似乎正在处理边缘案例
但是,我不认为这段代码是正确的。
在有多个&#34; *&#34;的情况下,它确实无法应对回溯。模式中的通配符。这需要一个递归的解决方案。
部分:
if(j<p.length() && p.charAt(j)=='*') {
ii=i;
jj=j;
j++;
没有多大意义。我认为它应该增加i
而不是j
。它可能会&#34;网格&#34;与else
部分的行为有关,但即使这样做,这也是一种令人费解的编码方式。
建议:
我会通过将通配符模式转换为正则表达式,然后使用Pattern
/ Matcher
进行匹配来处理此问题。
答案 3 :(得分:0)
我知道您在问有关BST的问题,但是老实说,还有一种使用正则表达式的方法(不是用于竞争性编程,而是足够稳定和快速地用于生产环境中):
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class WildCardMatcher{
public static void main(String []args){
// Test
String urlPattern = "http://*.my-webdomain.???",
urlToMatch = "http://webmail.my-webdomain.com";
WildCardMatcher wildCardMatcher = new WildCardMatcher(urlPattern);
System.out.printf("\"%s\".matches(\"%s\") -> %s%n", urlToMatch, wildCardMatcher, wildCardMatcher.matches(urlToMatch));
}
private final Pattern p;
public WildCardMatcher(final String urlPattern){
Pattern charsToEscape = Pattern.compile("([^*?]+)([*?]*)");
// here we need to escape all the strings that are not "?" or "*", and replace any "?" and "*" with ".?" and ".*"
Matcher m = charsToEscape.matcher(urlPattern);
StringBuffer sb = new StringBuffer();
String replacement, g1, g2;
while(m.find()){
g1 = m.group(1);
g2 = m.group(2);
// We first have to escape pattern (original string can contain charachters that are invalid for regex), then escaping the '\' charachters that have a special meaning for replacement strings
replacement = (g1 == null ? "" : Matcher.quoteReplacement(Pattern.quote(g1))) +
(g2 == null ? "" : g2.replaceAll("([*?])", ".$1")); // simply replacing "*" and "?"" with ".*" and ".?"
m.appendReplacement(sb, replacement);
}
m.appendTail(sb);
p = Pattern.compile(sb.toString());
}
@Override
public String toString(){
return p.toString();
}
public boolean matches(final String urlToMatch){
return p.matcher(urlToMatch).matches();
}
}
仍然可以实现一些优化(小写/大写区别,为要检查的字符串设置最大长度,以防止攻击者使您对4-GigaByte-String进行检查,...)。