Java实现通配符匹配

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));
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343