10. Regular Expression Matching

Implement regular expression matching with support for '.' and '*'.

'.' Matches any single character.
'*' Matches zero or more of the preceding element.

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", "a*") → true
isMatch("aa", ".*") → true
isMatch("ab", ".*") → true
isMatch("aab", "c*a*b") → true

这题首先审题得审一会, “.”的使用非常简单,就是可以匹配任意单一字符。“*”的使用是本题的关键,题目说星号可以匹配0个或者多个它的前一项字符,0个就是说星号加上它的前一项字符后整体从p字符串中消除,1个或者多个就是说星号加上它的前一项可以等于一个或者多个它的前一项字符。比如给出的例子的最后一个,之所以是true就是因为,“c*” 代表0个c, 然后“a*”代表两个a。所以才和aab匹配。理解了这一点,就明白这个字符串匹配的难点就在于星号所带来的不确定性。

这一题还是典型的递推关系,所以可以用递归recursion也可以用动态规划dynamic programming,当然后者在效率上具有明显的优势。但两种方法在基本思路的分析上还是一致的。以动态规划为主,介绍分析思路dp[i][j]表示s的前i位和p的前j位的匹配结果;总长度为dp[s.length+1][p.length+1]。
明显电平dp[0][0] = true.
1. 边界条件
i=0, j如果是奇数,就是false,如果是偶数且为*,就是true,因为偶数位如果都是*, 可以都消去。
j=0, i只能为0,否则就是false。
2. 递推关系
判断p的当前位:
p[j-1]如果是普通字符,那么如果s[i-1]==p[j-1], 此时dp[i][j] = dp[i-1][j-1];
p[j-1]如果是“.”, 那么dp[i][j] = dp[i-1][j-1];
p[j-1]如果是“*”,这种情况最为复杂,因为星号会带来匹配长度的不确定,那就只能分类讨论,如果匹配0个,那么dp[i][j-2]应该为true,这样才有意义,如果匹配一个,那么dp[i][j-1]应该为true,这样星号加前一项还是前一项,如果匹配多个,那么证明这一个星号已经在之前匹配过了,所以dp[i-1][j]应该为true,而且此时dp[i][j]如果想要为true,s[i-1]必须等于p[j-2]也就是星号的前一项,或者这个前一项为“.”。

方法一:动态规划dynamic programming
class Solution {
public:
    bool isMatch(string s, string p) {
        int lens = s.size();
        int lenp = p.size();
        // 如果p为空,s必须为空
        if (lenp == 0) return lens == 0;
        int i, j;
        bool dp[lens+1][lenp+1];
        //初始化
        dp[0][0] = true;
        for (i = 1; i < lens+1; i++) {
            dp[i][0] = false;
        }
        for (j = 1; j < lenp+1; j++) {
            if (j > 1 && p[j-1] == '*') dp[0][j] = dp[0][j-2];
            else dp[0][j] = false;
        }
        //递推
        for (i = 1; i < lens+1; i++) {
            for (j = 1; j < lenp+1; j++) {
                if (p[j-1] != '*' && p[j-1] != '.') {
                    if (s[i-1] == p[j-1]) dp[i][j] = dp[i-1][j-1];
                    else dp[i][j] = false;
                }
                else if (p[j-1] == '.') dp[i][j] = dp[i-1][j-1];
                else if (p[j-1] == '*' && j > 1) {
                    // *对应0个前字符
                    if (dp[i][j-2] == true) dp[i][j] = true;
                    // *对应1个前字符
                    else if (dp[i][j-1] == true) dp[i][j] = true;
                    // *对应多个前字符
                    else if (dp[i-1][j] == true && (s[i-1] == p[j-2] || p[j-2] == '.')) dp[i][j] = true;
                    else dp[i][j] = false;
                }
            }
        }
        return dp[lens][lenp];
    }
};
方法二:递归recursion
class Solution {
public:
    bool isMatch(string s, string p) {
        if (s.length() == 0){
            // s串匹配完合法的情况只有p为空,或是 "X*X*"的形式
            if (p.length() & 1) return false;
            else {
                for (int i = 1; i < p.length(); i += 2) {
                    if (p[i] != '*') return false;
                }
            }
            return true;
        }
        if (p.length() == 0) return false;
        if (p.length() > 1 && p[1] == '*') {
            //首项存在多个匹配的问题
            if (p[0] == '.' || s[0] == p[0]) {
                // match一个或者多个 || match0个
                return isMatch(s.substr(1), p) || isMatch(s, p.substr(2));
            } else return isMatch(s, p.substr(2)); //match0个
        } else {
            //首项没有多个匹配的问题,直接检测并跳过
            if (p[0] == '.' || s[0] == p[0]) {
                return isMatch(s.substr(1), p.substr(1));
            } else return false;
        }
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值