资讯专栏INFORMATION COLUMN

Leetcode 44 Wildcard Matching 通配符匹配

SimonMa / 1388人阅读

摘要:难度题目给出一个字符串和一个要求我们给出这个字符串是否匹配这个其中通配符跟我们平常见到的一样是和代表任意单个字符代表一个或多个字符这个题跟简单正则匹配比较类似可以跟这里面第二个解法一样采取类似的动态规划解法在里取中间某个确定的字符串序列将字

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

难度: Hard

题目给出一个字符串和一个pattern, 要求我们给出这个字符串是否匹配这个pattern. 其中通配符跟我们平常见到的一样, 是 ? 和 * . ?代表任意单个字符, * 代表一个或多个字符.
这个题跟Leetcode 10 简单正则匹配 比较类似, 可以跟这里面第二个解法一样, 采取类似的动态规划解法, 在pattern里取中间某个确定的字符串序列, 将字符串和pattern分别切分成两段再分别判定是否匹配.
例如, 字符串是 abc, 判定是否匹配到*b?, 我们可以抓住中间的b, 匹配到abc中的b, 将abc切分为["a","c"], 将*b?切分为 ["*","?"], 分别判定"a"和"*", 以及"c"和"?"是否匹配, 如果都匹配, 那么整个最终结果就是匹配的.
当然, 事情有时候不会这么顺利, 比如字符串出现多个b呢? 这时候需要尝试, pattern中的b到底是字符串中的哪一个b.

这里提出一种新的, 无递归的解法. 基本思路为:

将pattern中所有连续多个*置换为单个*, 比如a**a置换为a*a

跟Leetcode 10 的解法一样, 将pattern前后都确定的字符去跟输入字符串前后匹配, 比如字符串abc和模式a*c, 掐头去尾变成 b和*, 这期间如果有不同可以直接返回不匹配.

剩下的是有*的部分, 比如pattern可能是*a*bb*c*. 这样我们采取尽早匹配*之外字符的方式, 像上面那个pattern, 按顺序, 尽早匹配a, bb, 和c, 如果在字符串结束之前都匹配ok, 这样最终结果就是匹配成功的, 否则, 如果某个子串比如bb找不到, 或者还没全部匹配完就已经走到了字符串的末尾, 都算匹配失败.

AC的程序最终可以超越75%的算法, 如下:

public class Solution {
    public boolean isMatch(String s, String p) {
        // replace all redundent ** to *
        if (p.length() > 0) {
            StringBuffer sb = new StringBuffer();
            sb.append(p.charAt(0));
            for (int i = 1; i < p.length(); i++) {
                if (p.charAt(i) != "*" || p.charAt(i - 1) != "*") {
                    sb.append(p.charAt(i));
                }
            }
            p = sb.toString();
        }
        if (p.length() == 1 && p.charAt(0) == "*") {
            return true;
        }

        int slen = s.length();
        int plen = p.length();
        int ps = 0;
        int pp = 0;
        // trim left non-star element of s and p
        // "aabb","?*b" -> "abb","*b"
        while (pp < plen && ps < slen && p.charAt(pp) != "*") {
            if (p.charAt(pp) != "?" && p.charAt(pp) != s.charAt(ps)) {
                return false;
            }
            pp++;
            ps++;
        }
        int trimleft = pp;
        s = s.substring(trimleft);
        p = p.substring(trimleft);

        // if s and p is not empty
        // trim right non-star element of s and p
        // "abb","*b" -> "ab","*"
        if (s.length() > 0 && p.length() > 0) {
            slen = s.length();
            plen = p.length();
            ps = slen - 1;
            pp = plen - 1;
            while (pp >= 0 && ps >= 0 && p.charAt(pp) != "*") {
                if (p.charAt(pp) != "?" && p.charAt(pp) != s.charAt(ps)) {
                    return false;
                }
                pp--;
                ps--;
            }
            int trimright = plen - 1 - pp;
            s = s.substring(0, slen - trimright);
            p = p.substring(0, plen - trimright);
        }
        slen = s.length();
        plen = p.length();
        // length of s or length of p is zero judgement
        if (plen == 0) {
            if (slen > 0) {
                return false;
            } else {
                return true;
            }
        }
        if (slen == 0 && (plen > 1 || plen == 1 && p.charAt(0) != "*")) {
            return false;
        }
        ps = 0;
        pp = 0;
        int ptnl = 1;
        int ptnr = 1;
        int psl = 0;
        int psr = 0;
        // first and last character of p is star
        // p: *aa*bb* -> (aa,bb)
        // locate each of the non-star sub-patterns in s sequentially
        // if all satisfies, return true
        // otherwise false
        while (ptnl < plen && ptnr < plen) {
            // ptnl and ptnr designates left and right index of current
            // sub-pattern
            // find a sub-pattern
            while (p.charAt(ptnr) != "*") {
                ptnr++;
            }
            // find match in s
            for (int i = psl; i <= slen - (ptnr - ptnl); i++) {
                int j = ptnl;
                for (; j < ptnr; j++) {
                    if (s.charAt(i + (j - ptnl)) != p.charAt(j) && p.charAt(j) != "?") {
                        break;
                    }
                }
                if (j == ptnr) {
                    // matches current sub-pattern
                    psl = i;
                    psr = psl + (ptnr - ptnl);
                    break;
                }
            }
            if (psl == psr) {
                // no match for current sub-pattern
                return false;
            }
            // go to next position for next sub-pattern
            psl = psr;
            ptnr++;
            ptnl = ptnr;
        }
        return true;
    }

    public static void main(String[] args) {
        Solution s = new Solution();
        System.out.println(s.isMatch("bb", "?*?"));
        System.out.println(s.isMatch("b", "?*?"));
        System.out.println(s.isMatch("aa", "a"));
        System.out.println(s.isMatch("aa", "aa"));
        System.out.println(s.isMatch("aaa", "aa"));
        System.out.println(s.isMatch("aa", "*"));
        System.out.println(s.isMatch("aa", "a*"));
        System.out.println(s.isMatch("ab", "?*"));
        System.out.println(s.isMatch("aab", "c*a*b"));
        System.out.println(s.isMatch("aabbaab", "a*b"));
        System.out.println(s.isMatch("aabbbaaab", "a*b*b"));
        System.out.println(s.isMatch("aaabababaaabaababbbaaaabbbbbbabbbbabbbabbaabbababab", "*ab***ba**b*b*aaab*b"));
        System.out.println(s.isMatch("aaabaaaabbbbbbaaabbabbbbababbbaaabbabbabb", "*b*bbb*baa*bba*b*bb*b*a*aab*a*"));
        System.out.println(s.isMatch(
                "abbaabbbbababaababababbabbbaaaabbbbaaabbbabaabbbbbabbbbabbabbaaabaaaabbbbbbaaabbabbbbababbbaaabbabbabb",
                "***b**a*a*b***b*a*b*bbb**baa*bba**b**bb***b*a*aab*a**"));

        System.out.println(s.isMatch("bbbbbbbabbaabbabbbbaaabbabbabaaabbababbbabbbabaaabaab", "b*b*ab**ba*b**b***bba"));
        System.out.println(s.isMatch(
                "abbabaaabbabbaababbabbbbbabbbabbbabaaaaababababbbabababaabbababaabbbbbbaaaabababbbaabbbbaabbbbababababbaabbaababaabbbababababbbbaaabbbbbabaaaabbababbbbaababaabbababbbbbababbbabaaaaaaaabbbbbaabaaababaaaabb",
                "**aa*****ba*a*bb**aa*ab****a*aaaaaa***a*aaaa**bbabb*b*b**aaaaaaaaa*a********ba*bbb***a*ba*bb*bb**a*b*bb"));
    }
}

main中包含一部分测试用例.

这里, 我突然发现, 上面第一步, 将pattern中所有连续多个*置换为单个*, 虽然可以让pattern变得更简单, 但其实不是非常必要, 既然后面使用游标, 就可以略过连续多个的*. 比如上面接近最后有一句ptnr++, 实际上就是略过了其后的一个*, int ptnr=1; 就是略过了开头的一个*. 把这些都置换为略过连续的*, 即可. 改进后的程序如下:

public class Solution2 {
    public boolean isMatch(String s, String p) {
        int slen = s.length();
        int plen = p.length();
        int ps = 0;
        int pp = 0;

        // trim left non-star element of s and p
        // "aabb","?*b" -> "abb","*b"
        while (pp < plen && ps < slen && p.charAt(pp) != "*") {
            if (p.charAt(pp) != "?" && p.charAt(pp) != s.charAt(ps)) {
                return false;
            }
            pp++;
            ps++;
        }
        int trimleft = pp;
        s = s.substring(trimleft);
        p = p.substring(trimleft);

        // if s and p is not empty
        // trim right non-star element of s and p
        // "abb","*b" -> "ab","*"
        slen = s.length();
        plen = p.length();
        if (slen > 0 && plen > 0) {
            ps = slen - 1;
            pp = plen - 1;
            while (pp >= 0 && ps >= 0 && p.charAt(pp) != "*") {
                if (p.charAt(pp) != "?" && p.charAt(pp) != s.charAt(ps)) {
                    return false;
                }
                pp--;
                ps--;
            }
            int trimright = plen - 1 - pp;
            s = s.substring(0, slen - trimright);
            p = p.substring(0, plen - trimright);
        }
        slen = s.length();
        plen = p.length();
        // length of s or length of p is zero judgement
        if (plen == 0) {
            if (slen > 0) {
                return false;
            } else {
                return true;
            }
        }
        
        ps = 0;
        pp = 0;
        int ptnl = 0;
        int ptnr = 0;
        int psl = 0;
        int psr = 0;
        // skip preceding *
        while (ptnr < plen && p.charAt(ptnr) == "*") {
            ptnr++;
        }
        ptnl = ptnr;
        // first and last character of p is star
        // p: *aa*bb* -> (aa,bb)
        // locate each of the non-star sub-patterns in s sequentially
        // if all satisfies, return true
        // otherwise false
        while (ptnl < plen && ptnr < plen) {
            // ptnl and ptnr designates left and right index of current
            // sub-pattern
            // find a sub-pattern
            while (ptnr < plen && p.charAt(ptnr) != "*") {
                ptnr++;
            }
            // find match in s
            for (int i = psl; i <= slen - (ptnr - ptnl); i++) {
                int j = ptnl;
                for (; j < ptnr; j++) {
                    if (s.charAt(i + (j - ptnl)) != p.charAt(j) && p.charAt(j) != "?") {
                        break;
                    }
                }
                if (j == ptnr) {
                    // matches current sub-pattern
                    psl = i;
                    psr = psl + (ptnr - ptnl);
                    break;
                }
            }
            if (psl == psr) {
                // no match for current sub-pattern
                return false;
            }
            // go to next position for next sub-pattern
            psl = psr;
            while (ptnr < plen && p.charAt(ptnr) == "*") {
                ptnr++;
            }
            ptnl = ptnr;
        }
        return true;
    }
}

最终提交结果速度又快了很多, 在99.49%的提交之前:

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/66531.html

相关文章

  • [Leetcode] Wildcard Matching 配符匹配

    摘要:当我们遇到一个时,因为之后可能要退回至该位置重新匹配,我们要将它的下标记录下来,比如。但是,当我们连续遇到两次的情况,如何保证我还是能继续匹配,而不是每次都退回导致循环呢所以我们还要记录一个,用来记录用上一个连续匹配到的中的下标。 Wildcard Matching Implement wildcard pattern matching with support for ? and ...

    tainzhi 评论0 收藏0
  • leetcode-44. Wildcard Matching

    摘要:正则由于的存在,所以有多种状态,中间状态储存都需要记录下来。然后以这些状态为动态的中转,继续判断到最后。最后正则匹配字符串是否成功的判断依据,就是正则字符串的最大,是否出现在遍历到最后的状态列表中。 题目阐释: 正则匹配字符串,用程序实现 关键理解: 正则匹配,动态规划思想,一个个向后追溯,后面的依赖前面的匹配成功。 正则和待匹配的字符串长度不一,统一到正则字符串的index索引上,每...

    leanxi 评论0 收藏0
  • Wildcard Matching

    摘要:题目链接这道题还是可以用的方法,用的数组来解,空间复杂度较高。和不同,这道题的符号和前面的没有关系,不需要一起考虑。最坏的情况下,间隔出现且每个都要匹配很多字符,设一个平均匹配里面个字符,。其中,是的长度,是的长度。 Wildcard Matching 题目链接:https://leetcode.com/problems...这道题还是可以用Regular Expression Mat...

    galaxy_robot 评论0 收藏0
  • [LintCode/LeetCode] Wildcard Matching

    摘要:递归和动规的方法没有研究,说一下较为直观的贪心算法。用和两个指针分别标记和进行比较的位置,当遍历完后,若也遍历完,说明完全配对。当之前出现过,且此时和完全无法配对的时候,就一起退回在和配对过的位置。再将和逐个加继续比较,并将后移。 Problem Implement wildcard pattern matching with support for ? and *. ? Matche...

    Ethan815 评论0 收藏0
  • Leetcode 10 Regular Expression Matching 简单正则匹配

    摘要:难度这道题要求我们实现简单的正则表达式的匹配只要求普通字符的匹配了解正则的同学都清楚代表任意单个字符代表个或多个前面的字符比如可以匹配到空字符串也可以匹配等等题目还要求我们判定正则是否匹配给定的字符串要判定整个字符串而不是其中一部分匹配就算 Implement regular expression matching with support for . and *. . Matche...

    OnlyLing 评论0 收藏0

发表评论

0条评论

SimonMa

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<