Wildcard Matching

本文介绍了一种高效的通配符匹配算法,支持'?'和'*'两种通配符,通过优化递归和回溯策略,避免了不必要的重复计算,提高了匹配效率。

原题:

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;
    }





                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值