通配符匹配字符串 Wildcard Matching

来源:互联网 发布:火箭队 机械 知乎 编辑:程序博客网 时间:2024/04/29 22:19

问题:实现支持?和*两个通配符的字符串匹配函数。

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

回忆一下类似的一个问题《简单的正则表达式匹配 Regular Expression Matching》,注意其中的区别:两个问题中*所具备的匹配能力是不同的。

思路一:递归求解。虽然已经把重复出现的*过滤,不过超时了

class Solution {public:    bool isMatch(const char *s, const char *p) {        if(s == NULL || p == NULL)            return false;        return isValid(s, p);    }        bool isValid(const char *s, const char *p)    {        if(*p == '\0')            return *s == '\0';                    if(*p == '?')            return isValid(s+1, p+1);        else if(*p != '*')        {            if(*p == *s)                return isValid(s+1, p+1);            else                return false;        }        else        {            p++;            while(*p == '*')                p++;            while(*s != '\0')            {                if(isValid(s, p))                    return true;                s++;            }            return isValid(s, p);        }    }};
思路二:动态规划法

设置状态量H[pn+1][sn+1]。H[i][j]表示p的前i个字符能否匹配成功s的前j个字符。

递推关系:如果H[i-1][j-1]=1,若p[i]='?'或者p[i]==s[j],那么H[i][j]为1;若p[i]='*',那么H[i][j-1]到H[i][sn]都为1。

初始条件:H[0][0]=1。

注意:必须要提前把不可能匹配的情况排除,否则会超时。当p串中非*字符的个数大于0且少于s串的字符个数时,匹配不可能成功。

class Solution {public:    bool isMatch(const char *s, const char *p) {        if(s == NULL || p == NULL)            return false;                    //计数:记录p串的字符个数(pn)、s串的字符个数(sn)、p串中*的个数(stars)        const char *p1;        p1 = p;        int stars = 0;        while(*p1 != 0)        {            if(*p1 == '*')                stars++;            p1++;        }        int pn = p1 - p;                p1 = s;        while(*p1 != 0)            p1++;        int sn = p1 - s;                if(pn == stars && stars > 0) //若p串中只有*,一定匹配            return true;        if(pn - stars > sn) //若p串中非*字符的个数多于s串,不可能匹配            return false;                int H[pn+1][sn+1];        memset(H,0 ,sizeof(H));        H[0][0] = 1;        for(int i=1;i<=pn;i++)        {            if(p[i-1] != '*')                break;            H[i][0] = 1;        }                for(int i=1;i<=pn;i++)        {            for(int j=1;j<=sn;j++)            {                if(H[i-1][j-1] == 1)                {                    if(p[i-1] == '?' || p[i-1] == s[j-1])                    {                        H[i][j] = 1;                    }                    else if(p[i-1] == '*')                    {                        for(int k=j-1;k<=sn;k++)                            H[i][k] = 1;                    }                }            }        }        //当p串以*结尾时,与s的匹配有可能提前结束。        int last;        for(last=pn;last>=0;last--)            if(H[last][sn] == 1)                break;        last++;        while(last<=pn && p[last-1] == '*')            last++;        if(last == pn+1)            return true;        return H[pn][sn] == 1;    }};

思路二的优化:上面的动态规划中对'*'星号的递推处理不太恰当,使得DP之后还要再处理一下。现在改进一下DP的递推方法:

在DP的二重循环遍历到H[i][j](即判断p串的前i项是否匹配s串的前j项)时,

如果有p[i]='?'或者p[i]==s[j] ,并且,H[i-1][j-1]=1,那么H[i][j]为1。

如果有p[i]='*',并且,H[i-1][j]=1 ,那么H[i][j]~H[i][sn]都为1。

另外初始情况时,要把*的情况考虑。

class Solution {public:    bool isMatch(const char *s, const char *p) {        if(s == NULL || p == NULL)            return false;                //计数:记录p串的字符个数(pn)、s串的字符个数(sn)、p串中*的个数(stars)        const char *p1;        p1 = p;        int stars = 0;        while(*p1 != 0)        {            if(*p1 == '*')                stars++;            p1++;        }        int pn = p1 - p;                p1 = s;        while(*p1 != 0)            p1++;        int sn = p1 - s;                if(pn == stars && stars > 0)  //若p串中只有*,一定匹配            return true;        if(pn - stars > sn)  //若p串中非*字符的个数多于s串,不可能匹配            return false;                int H[pn+1][sn+1];        memset(H,0 ,sizeof(H));        H[0][0] = 1;        for(int i=1;i<=pn;i++)        {            if(p[i-1] != '*')                break;for(int j=0;j<=sn;j++) //当p串开头就有*H[i][j] = 1;        }                for(int j=1;j<=sn;j++)        {            for(int i=1;i<=pn;i++)            {if ((H[i-1][j-1] == 1) && (p[i-1] == '?' || p[i-1] == s[j-1]))                    {                        H[i][j] = 1;                    }                    else if(H[i-1][j] == 1 && p[i-1] == '*')                    {                        for(int k=j;k<=sn;k++)                            H[i][k] = 1;                    }            }        }        return H[pn][sn] == 1;    }};

思路三:在网上看到的优化方法。记录前一个*字符的位置,优先进行单字符匹配,当失败的时候再回来进行通配。

class Solution {public:bool isMatch(const char *s, const char *p) {    if(!s && !p) return true;    const char *star_p=NULL,*star_s=NULL;    while(*s)    {        if(*p == '?' || *p == *s)        {            ++p,++s;        }else if(*p == '*')        {            //skip all continuous '*'            while(*p == '*') ++p;            if(!*p) return true; //if end with '*', its match.            star_p = p; //store '*' pos for string and pattern            star_s = s;        }else if((!*p || *p != *s)  && star_p)        {            s = ++star_s; //skip non-match char of string, regard it matched in '*'            p = star_p; //pattern backtrace to later char of '*'        }else            return false;    }    //check if later part of p are all '*'    while(*p)        if(*p++ != '*')            return false;    return true;}};
0 0
原创粉丝点击