public class WildcardMatching {
/**
* 失效回溯法
*
* 思想1:对于通配符匹配方案,我们主要的难点问题是在于通配符*的匹配,
* 所以首要问题我们要定位到*所在的位置,定位到*之后我们再在此处做文章
* 思想2:单值通配符?姑且忽略,我们只要把他当作任意字符处理即可,让他等价于任意字符。
* 思想3:假设目标串和模板串都是普通字符串,不含有任何通配符,那么此时我们的比较方式应该是逐个字符的比较方式
* 思想4:假设另一种特殊情况,我们的模板串是目标串中间穿插的通配符,此时我们在处理的过程中可以跳过通配符
* 思想5:在思想4的基础上我们引申一下,我们思考一下我们应该如何跳过通配符,如果只是单纯的忽略,对我们解决问题没有任何意义,
* 假设我们模板串中只有一个通配符,当前前面的若干字符匹配成功,此时遇见通配符,此时
* 比较目标串当前索引开始的子串与模板串当前通配符之后的子串。如果子串能够匹配,那么自然而然的忽略了通配符
* 思想6:在思想5的基础上我们继续引申,如果子串匹配失效,能够证明此时的匹配完全失效吗?
* 不能,因为我们通配符之后的子串可以只匹配目标串的最后几位,我们的通配符可以代替中间的所有字符。
* 那么问题来了,我们接下来该怎么办?
* 思想7:在思想6的基础上我们继续引申,假设我们模板串的通配符两端能够与目标串的首尾匹配,那么我们该如何解决中间的内容匹配呢?
* 我们再回想一下思想5中忽略通配符的过程,因为我们通配符就是用来忽略字符的,我们为什么要忽略他呢,
* 问题出在了我们思想4中的假设,因为我们假设的是通配符是多余的,但是仔细想一下,
* 其实通配符多余只是一种通配符匹配到0个字符的情况,那么通配符如果匹配多个字符该如何操作呢,
* 此时,我们是不是应该忽略目标串中的字符,那么怎么忽略呢?我们再想一想思想5中的子串比较,
* 我们是不是可以通过缩短目标串的子串来达到忽略的效果呢?可以达到,那么问题又来了,怎么缩短呢?
* 思想8:在思想7的基础上我们再思考,假设两个子串匹配失效之后,我们怎么能够回到起点进行忽略操作?
* 我们是不是就应该对目标子串的起始位置进行记录呢,这样匹配失败后我能够回到最初的起点。
* 于是目标子串回到起点后加1后便达到了忽略一个字符的效果。但是目标子串忽略一个字符表示这个字符是被通配符抵消掉的,
* 那接下来的目标子串应该是与模板串的哪部分比较呢,是不是也应该是通配符后边的子串。
* 那么我们同样需要对通配符的位置进行记录。我们可以将这两个标记称为回溯点。
* 思想9:在思想8的回溯思路上,我们凡是遇到失效,就回溯到回溯点的位置,直到目标串处理完毕
* 思想10:目标串匹配结束后,已经确定目标串满足我们模板串,此时需要检测模板串是否满足目标串,
* 也就是说模板串是否还有残余串,如果残余串是*放行,否则阻断。
* 当残余串检测完毕之后,此时如果模板串检测到的索引刚好等于模板串的索引,表示模板串满足目标串。
* 到此,我们整个问题的解决思路已经明确。
* 思想11:前面的思想我们都是基于单个通配符的基础上进行引申的,那么多个通配符又能否满足呢?仔细思考一下。是完全可以,
* 因为如果能够匹配到下一个通配符的位置,那么下一个通配符将会接替前一个通配符的监听,前一个通配符的职责已经完成。
* 思想12:在思想2中我们忽略的单值通配符应该如何处理呢,我们前文说过,让他等价于任何字符,也就是说,
* 在我们判断目标串中某个字符和模板串中的某个字符是否相等时,如果模板串中的字符是单值通配符,直接按照匹配成功,放行即可。
* 至此,所有的疑点都已经一一击破,真相已经水落石出。
* @param s 待匹配字符串
* @param p 通配符模板字符串
* @return 匹配结果
*/
boolean isMatch(String s, String p) {
// i 用来记录s串检测的索引的位置
int i = 0;
// j 用来记录p串检测的索引的位置
int j = 0;
// 记录 待测串i的回溯点
int ii = -1;
// 记录 通配符*的回溯点
int jj = -1;
// 以s字符串的长度为循环基数,用i来记录s串当前的位置
while (i < s.length()) {
// 用j来记录p串的当前位置,检测p串中j位置的值是不是通配符*
if (j < p.length() && p.charAt(j) == '*') {
// 如果在p串中碰到通配符*,复制两串的当前索引,记录当前的位置,并对p串+1,定位到非通配符位置
ii = i;
jj = j;
j++;
}
else if (j < p.length() // 检测p串是否结束
&& (s.charAt(i) == p.charAt(j) // 检测两串当前位置的值是否相等
|| p.charAt(j) == '?')) { // 检测p串中j位置是否是单值通配符?
// 如果此时p串还在有效位置上,那么两串当前位置相等或者p串中是单值通配符,表明此时匹配通过,两串均向前移动一步
i++;
j++;
}
else {
// 如果在以上两种情况下均放行,表明此次匹配是失败的,那么此时就要明确一点,s串是否在被p串中的通配符*监听着,
// 因为在首次判断中如果碰到通配符*,我们会将他当前索引的位置记录在jj的位置上,
// 如果jj = -1 表明匹配失败,当前s串不在监听位置上
if (jj == -1) return false;
// 如果此时在s串在通配符*的监听下, 让p串回到通配符*的位置上继续监听下一个字符
j = jj;
// 让i回到s串中与通配符对应的当前字符的下一个字符上,也就是此轮匹配只放行一个字符
i = ii + 1;
}
}
// 当s串中的每一个字符都与p串中的字符进行匹配之后,对p串的残余串进行检查,如果残余串是一个*那么继续检测,否则跳出
while (j < p.length() && p.charAt(j) == '*') j++;
// 此时查看p是否已经检测到最后,如果检测到最后表示匹配成功,否则匹配失败
return j == p.length();
}
public static void main(String args[]) {
String s = "aabdsfgbcde";
String p = "aa*bcde";
WildcardMatching wcm = new WildcardMatching();
System.out.println(wcm.isMatch(s, p));
}
}
Java实现通配符匹配
©著作权归作者所有,转载或内容合作请联系作者
- 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
- 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
- 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...