[leetcode] Wildcard Matching 通配符匹配

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

同时在lintcode上也有http://www.lintcode.com/zh-cn/problem/wildcard-matching/

也是《剑指offer》中的题目

问题描述:

判断两个可能包含通配符“?”和“*”的字符串是否匹配。匹配规则如下:

'?' 可以匹配任何单个字符。
'*' 可以匹配任意字符串(包括空字符串)。

两个串完全匹配才算匹配成功。
接口如下:其中s为待匹配串(不含'?'和'*'),p为模式串(含'?'和'*')
bool isMatch(const char *s, const char *p)

一些例子:

isMatch("aa", "*") → true
isMatch("aa", "a*") → true
isMatch("ab", "?*") → true
isMatch("aab", "c*a*b") → false
(一)看到题目我最先想到的是dfs暴力枚举所有情况,想想也知道会TLE,不出所料。感觉自己真是low爆了!
(二)接着往下想,会考虑到使用动态规划,想想字符串的编辑距离问题,那么这个题目的动态转移方程应该也是类似推导出来的。不妨令dp[i][j]表示s(0,i)的子串是否与p(0,j)的子串完全匹配,那么我们来看看dp[i][j]与dp[i-1][j], dp[i][j-1], dp[i-1][j-1]的关系:
例如:s = "aab", p = "a?*b", 那么可得出如下表格:
[leetcode] Wildcard Matching 通配符匹配_第1张图片 [leetcode] Wildcard Matching 通配符匹配_第2张图片
可以发现dp[i][j]为真有三种情况:(1) dp[i-1][j-1]为真,且s[i] 与 s[j]匹配;(2) dp[i-1][j]为真,且p[j]为'*',此时用'*'匹配多个字符;(3) dp[i][j-1]为真,且p[j]为'*',此时'*'匹配空字符。
故有:dp[i][j] = ((dp[i-1][j-1] && equal(s+i,p+j)) ||  (dp[i-1][j] && (*(p+j)=='*')) || (dp[i][j-1] && (*(p+j)=='*')));
此处为了方便,我将dp[0][0~n]与dp[0~m][0]初始化为空字符串与p的匹配情况和s与空字符串的匹配情况,具体代码如下:
class Solution {
public:
    /**
     * @param s: A string 
     * @param p: A string includes "?" and "*"
     * @return: A boolean
     */
    bool equal(const char *s, const char *p){
        if(*s == *p) return true;
        if(*p == '?' || *p == '*')   return true;
        return false;
    }
     
    bool isMatch(const char *s, const char *p) {
        // write your code here
        if(s == NULL && p == NULL)   return true;
        int m = strlen(s);
        int n = strlen(p);
        if(m == 0 && n == 0)   return true;
        vector tmp(n+1,false);
        vector > dp(m+1,tmp);
        dp[0][0] = true;
        for(int i=0; i

(三)后来看了discuss才发现还可以使用 贪心算法,不仅时间上有优化,还将空间压缩至O(1)。
主要思想是:从两个字符串的起始位置开始匹配,遇到通配符 '*' 时,优先考虑让其匹配空字符, 但记录该 ’*‘ 的位置,以及对应的s指针的位置,然后继续往后匹配;直到无法继续,则返回上一个 ’*‘ 的位置,考虑让其匹配1个字符,然后继续往后匹配;直到无法匹配,则重新返回上一个 ’*‘ 的位置,考虑让其匹配2个字符。。。。。。直到s的指针到达终点。此时,若当前p指针后面均为’*‘,则返回true,否则返回false。
代码如下:
class Solution {
public:
    /**
     * @param s: A string 
     * @param p: A string includes "?" and "*"
     * @return: A boolean
     */
    // greedy method
    bool isMatch(const char *s, const char *p) {
        // write your code here
        if(s == NULL && p == NULL)   return true;
        int m = strlen(s);
        int n = strlen(p);
        if(m == 0 && n == 0)   return true;
        int si = 0, pi = 0;
        int xidx = -1, mtch = -1;
        while(si < m){
            if(pi < n && (*(p+pi)=='*')){
                xidx = pi++;
                mtch = si;   // si对应xidx的位置
            }else if(pi < n && (*(s+si) == *(p+pi) || *(p+pi) == '?')){
                ++ si;
                ++ pi;
            }else if(xidx > -1){  // 上一个 '*' 的位置
                pi = xidx + 1;
                si = ++ mtch;
            }else{
                return false;
            }
        }
        while(pi < n && (*(p+pi) == '*'))  ++ pi;
        return (pi == n);
    }
};



你可能感兴趣的:(leetcode)