关闭

[Leetcode] 44. Wildcard Matching 解题报告

标签: LeetcodeWildcard Matching解题报告Dynamic ProgrammingBacktracking
146人阅读 评论(0) 收藏 举报
分类:

题目

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

思路

拿到这样的题目描述,不难想到动态规划。不过对于这道题目,动态规划的运行效率反而不是最优的,主要是因为没有利用到’*'的威力。具体看下面三种思路的分析及比较。

1、动态规划:定义dp[i][j]为一个布尔变量,代表s的前i个字符是否可以和p的前j个字符完全匹配成功。则递推方程如下:

      1)如果p[j] == '*',则该‘*'即可以出现0次,也可以出现至少1次,即dp[i+1][j+1] = dp[i+1][j] || dp[i][j+1];

      2)如果p[j] != '*',则完全可以匹配的条件是:s和t的当前最后一个字符可以匹配,s和t除去当前最后字符之外,其余前面的字符也可以完全匹配。

2、双线性扫描:考虑到‘*’可以匹配任意多个字符(也就是说,一旦出现‘*’,则此时所有s中已经扫描到的位置都可以被匹配),那么我们可以记录并不断更新每个‘*’至少需要匹配的字符长度。具体做法是:当出现一个‘*’时,则记录当前s字符串已经被扫描到的位置,再接着试图匹配s和t后面的字符位置。一旦发现后面位置无法匹配,则返回到这里,将不匹配的字符用当前最后一个‘*’来匹配(也就是说,‘*’至少需要匹配的字符长度一旦在后面无法匹配时会被迫递增)。最后再判断p的指针到最后都是‘*’,如果是,则可以匹配,否则就不可以匹配。(此解法参考了小榕流光的博客:http://blog.csdn.net/qq508618087/article/details/51044029)。

3、深度优先搜索:本质上该方法还是和双线性扫描的思路一致,只是用了深搜的实现方式。在遇到一个‘*’的时候,我们还是试图去寻找该‘*’至少需要匹配的字符串长度,以便于为后面尝试可能的匹配留下最大的搜索空间。此时将dfs的返回值定义为匹配的三种不同状态(见下面代码中的注释),以便于处理平凡情况以及有效剪枝。

实现发现,思路2和3的运行效率要远远高于思路1。

代码

1、动态规划:

class Solution {
public:
    bool isMatch(string s, string p) 
    {
        int n1 = s.length(), n2 = p.length();
        vector<vector<bool>> dp(n1+1, vector<bool>(n2+1, false));
        dp[0][0] = true;
        for(int j = 0; j < n2; ++j)
        {
            if(p[j] == '*')
                dp[0][j+1] = dp[0][j];
        }
        for(int i = 0; i < n1; ++i)
        {
            for(int j = 0; j < n2; ++j)
            {
                if(p[j] == '*')
                    // dp[i+1][j] means p[j] matches 0 time
                    // dp[i][j+1] means p[j] matches at least 1 time
                    dp[i+1][j+1] = dp[i+1][j] || dp[i][j+1];
                else
                    // dp[i][j] means the whether the previous can be matched
                    // (s[i] == p[j] || p[j] == '?') means whether the last charchater can be matched
                    dp[i+1][j+1] = dp[i][j] && (s[i] == p[j] || p[j] == '?');
            }
        }
        return dp[n1][n2];
    }
};
2、双线性扫描:

class Solution {
public:
    bool isMatch(string s, string p) 
    {
        int preS = -1, preP = -1, i = 0, j = 0;
        int len1 = s.size(), len2 = p.size();  
        while(i < len1)  
        {  
            if(s[i] == p[j] || p[j] == '?')     // matched
                i++, j++;  
            else if(p[j] == '*')                // at least we can match s[0, i]
                preS = i + 1, preP = j++;  
            else if(preP == -1)                 // no previous matched case exists
                return false;  
            else                                // re-match from here
                i = preS, j = preP;  
        }  
        while(p[j]=='*')                        // skip the more than necessary '*'
            j++;  
        return i == len1 && j == len2;
    }
};

3、深度优先搜索:

class Solution {
public:
    bool isMatch(string s, string p) 
    {
        return dfs(s, p, 0, 0) == 2;
    }
private:
    int dfs(string& s, string& p, int si, int pi)
    {
        if (si == s.size() and pi == p.size()) return 2;    // matched
        if (si == s.size() and p[pi] != '*') return 0;      // unmatched: reached the end of s
        if (pi == p.size()) return 1;                       // unmatched: not reached the end of s
        if (p[pi] == '?' or s[si] == p[pi])
            return dfs(s, p, si+1, pi+1);
        if (p[pi] == '*') {                                 // calculate the shortest length p[pi] need to cover
            if (pi+1 < p.size() and p[pi+1] == '*') 
                return dfs(s, p, si, pi+1);                 // skip duplicate '*'
            for(int i = 0; i <= s.size()-si; ++i) 
            {
                int ret = dfs(s, p, si+i, pi+1);
                if (ret == 0 or ret == 2)                   // reached the end of s, so return immediately
                    return ret; 
            }
        }
        return 1;
    }
};

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:48222次
    • 积分:4270
    • 等级:
    • 排名:第7378名
    • 原创:389篇
    • 转载:10篇
    • 译文:2篇
    • 评论:16条
    最新评论