LeetCode 44 Wildcard Matching (通配符匹配 记忆化搜索 剪枝 推荐)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Tc_To_Top/article/details/88946074

Given an input string (s) and a pattern (p), 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).

Note:

  • s could be empty and contains only lowercase letters a-z.
  • p could be empty and contains only lowercase letters a-z, and characters like ? or *.

Example 1:

Input:
s = "aa"
p = "a"
Output: false
Explanation: "a" does not match the entire string "aa".

Example 2:

Input:
s = "aa"
p = "*"
Output: true
Explanation: '*' matches any sequence.

Example 3:

Input:
s = "cb"
p = "?a"
Output: false
Explanation: '?' matches 'c', but the second letter is 'a', which does not match 'b'.

Example 4:

Input:
s = "adceb"
p = "*a*b"
Output: true
Explanation: The first '*' matches the empty sequence, while the second '*' matches the substring "dce".

Example 5:

Input:
s = "acdcb"
p = "a*c?b"
Output: false

题目链接:https://leetcode.com/problems/wildcard-matching/

题目分析:与正则匹配比较类似,本题不用记忆化会直接超时,多个连续的'*'可直接缩成1个,功能不会产生变化

25ms,时间击败76.9% (运行时间不是很稳定,21ms~28ms)

class Solution {
    
    public boolean isFirstPosMatch(String s, int spos, String p, int ppos) {
        return (spos < s.length() &&
            (s.charAt(spos) == p.charAt(ppos) ||
             p.charAt(ppos) == '?'));
    }
    
    public boolean isMatch(String s, String p) {
        int[][] match = new int[s.length() + 2][p.length() + 2];
        return isMatchHelper(s, 0, p, 0, match);
    }
    
    public boolean isMatchHelper(String s, int spos, String p, int ppos, int[][] match) {
        if (match[spos][ppos] != 0) {
            return match[spos][ppos] == 1;
        }
        if (ppos == p.length()) {
            match[spos][ppos] = spos == s.length() ? 1 : -1;
            return match[spos][ppos] == 1;
        }
        if (p.charAt(ppos) != '*') {
            match[spos][ppos] = isFirstPosMatch(s, spos, p, ppos) && isMatchHelper(s, spos + 1, p, ppos + 1, match) ? 1 : -1;
            return match[spos][ppos] == 1;
        }
        while (ppos < p.length() && p.charAt(ppos) == '*') {
            ppos++;
        }
        while (spos < s.length()) {
            match[spos][ppos] = isMatchHelper(s, spos, p, ppos, match) ? 1 : -1;
            if (match[spos][ppos] == 1) {
                return true;
            }
            spos++;
        }
        match[spos][ppos] = isMatchHelper(s, spos, p, ppos, match) ? 1 : -1;
        return match[spos][ppos] == 1;
    }
}

最后的while循环看上去是比较耗时的,可以从中挖掘一些剪枝,通配符匹配和正则匹配的一大不同就是通配符匹配中模式串的确定字符必须要在主串上严格匹配(正则可通过'*'消去前一项),于是想到用两个cnt数组分别记录s和p中字符串的个数,设当前匹配到了s的第i项s[i],若pcnt[s[i]] > scnt[s[i]],则说明s串接下来的s[i]字符已不够p串匹配了,故此时的'*'不能消掉s[i]

加了这个剪枝后,6ms,时间击败97.9%

class Solution {
    
    public boolean isFirstPosMatch(String s, int spos, String p, int ppos) {
        return (spos < s.length() &&
            (s.charAt(spos) == p.charAt(ppos) ||
             p.charAt(ppos) == '?'));
    }
    
    public boolean isMatch(String s, String p) {
        int[] scnt = new int[26];
        int[] pcnt = new int[26];
        for (int i = 0; i < s.length(); i++) {
            scnt[s.charAt(i) - 'a']++;
        }
        for (int i = 0; i < p.length(); i++) {
            char ch = p.charAt(i);
            if (ch >= 'a' && ch <= 'z') {
                pcnt[ch - 'a']++;
            }
        }
        int[][] match = new int[s.length() + 2][p.length() + 2];
        return isMatchHelper(s, 0, p, 0, match, scnt, pcnt);
    }
    
    public boolean isMatchHelper(String s, int spos, String p, int ppos, int[][] match, int[] scnt, int[] pcnt) {
        if (match[spos][ppos] != 0) {
            return match[spos][ppos] == 1;
        }
        if (ppos == p.length()) {
            match[spos][ppos] = spos == s.length() ? 1 : -1;
            return match[spos][ppos] == 1;
        }
        if (p.charAt(ppos) != '*') {
            boolean ok = isFirstPosMatch(s, spos, p, ppos);
            if (ok) {
                scnt[s.charAt(spos) - 'a']--;
                if (p.charAt(ppos) != '?') {
                    pcnt[p.charAt(ppos) - 'a']--;
                }
                ok &= isMatchHelper(s, spos + 1, p, ppos + 1, match, scnt, pcnt);
                if (p.charAt(ppos) != '?') {
                    pcnt[p.charAt(ppos) - 'a']++;
                }
                scnt[s.charAt(spos) - 'a']++;
            }
            match[spos][ppos] = ok ? 1 : -1;
            return ok;
        }
        while (ppos < p.length() && p.charAt(ppos) == '*') {
            ppos++;
        }
        while (spos < s.length() && pcnt[s.charAt(spos) - 'a'] <= scnt[s.charAt(spos) - 'a']) {
            match[spos][ppos] = isMatchHelper(s, spos, p, ppos, match, scnt, pcnt) ? 1 : -1;
            if (match[spos][ppos] == 1) {
                return true;
            }
            scnt[s.charAt(spos) - 'a']--;   
            spos++;
        }
        match[spos][ppos] = isMatchHelper(s, spos, p, ppos, match, scnt, pcnt) ? 1 : -1;
        return match[spos][ppos] == 1;
    }
}

猜你喜欢

转载自blog.csdn.net/Tc_To_Top/article/details/88946074