[Leetcode] 44. Wildcard Matching 解题报告

来源:互联网 发布:网络电话卡违法吗 编辑:程序博客网 时间:2024/06/10 11:49

题目

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") → falseisMatch("aa","aa") → trueisMatch("aaa","aa") → falseisMatch("aa", "*") → trueisMatch("aa", "a*") → trueisMatch("ab", "?*") → trueisMatch("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
原创粉丝点击