Regular Expression Matching

一. 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

Difficulty:Hard

TIME:TIMEOUT

解法一(深度优先搜索)

这道题主要是要理解匹配的过程,就是要把匹配的过程分成几种情况分别讨论,最简单的做法当然是用递归来求解,以下就对递归过程做一个描述。

首先是有*号的情况,也就是匹配0次或者1次以上:

  • 如果匹配0次,则isMatch(s,p) = isMatch(s,p.substr(2))
  • 如果匹配一次以上,则isMatch(s,p) = isMatch(s.substr(1),p)

然后是没有*号的情况,也就是只匹配一次:

  • 则isMatch(s,p) = isMatch(s.substr(1),p.substr(1))
void dfs(string s, int ss, string p, int pp, bool &result) {
    if (pp >= p.size() && ss >= s.size())
        result = true;
    if (result || pp >= p.size())
        return;
    if (pp + 1 < p.size() && p[pp + 1] == '*') { //有*的情况
        dfs(s, ss, p, pp + 2, result); //必定可以跳过
        if ((p[pp] == '.' || p[pp] == s[ss]) && ss + 1 <= s.size())
            dfs(s, ss + 1, p, pp, result); //但不一定能匹配一次以上
    }
    else if ((p[pp] == '.' || p[pp] == s[ss]) && ss + 1 <= s.size())
        dfs(s, ss + 1, p, pp + 1, result); //没有*的情况
}
bool isMatch(string s, string p) {
    bool result = false;
    dfs(s, 0, p, 0, result);
    return result;
}

代码的时间复杂度应该约为 O(2n)

解法二(动态规划)

一开始压根没有想到这道题能用动态规划来解,但确实可以这样,因为这道确实存在着最优子结构,而且和之前做过的Distinct Subsequences十分相似。

首先使用一个二维数组dp,第一维表示字符串s的长度,而第二维表示字符串p的长度(注意是长度而不是下标),值为true或false,也就是p的前缀能否匹配s的前缀。

为什么要采用长度而不是下标呢,首先一点处理空串会简单很多(如果采用下标,空串要单独处理),第二点是可以避免很多的边界条件判断(之前要处理下标是-1的状态,现在只需要处理下标是0的状态就行了)。因此代码会简洁不是一点,Distinct Subsequences的处理方式也是同样的道理 。

首先处理空串的情况,很容易理解dp[0][0]为true,然后就是dp[0][j]的情况,也容易想到可以一直匹配成功直到出现第一个没有带*号的字符。而具体的最优子结构我直接用LeetCode上面的其他人写的作为参照,将会在代码中给出:

/**
 * f[i][j]: if s[0..i-1] matches p[0..j-1]
 * if p[j - 1] != '*'
 *      f[i][j] = f[i - 1][j - 1] && s[i - 1] == p[j - 1]
 * if p[j - 1] == '*', denote p[j - 2] with x
 *      f[i][j] is true iff any of the following is true
 *      1) "x*" repeats 0 time and matches empty: f[i][j - 2]
 *      2) "x*" repeats >= 1 times and matches "x*x": s[i - 1] == x && f[i - 1][j]
 * '.' matches any single character
 */
bool isMatch(string s, string p) {
    vector<vector<bool>> dp(s.size() + 1, vector<bool>(p.size() + 1, false));
    dp[0][0] = true;
    for (int i = 1; i <= p.size(); i++) { //先处理空串的情况
        if (p[i - 1] == '*' && dp[0][i - 2])
            dp[0][i] = true;
    }
    for (int i = 1; i <= s.size(); i++) {
        for (int j = 1; j <= p.size(); j++) {
            if (j < p.size() && p[j] == '*') {
                if(dp[i][j - 1] || ((s[i - 1] == p[j - 1] || p[j - 1] == '.') && dp[i - 1][j + 1]))
                    dp[i][j + 1] = true;
            }
            else if (dp[i - 1][j - 1] && (s[i - 1] == p[j - 1] || p[j - 1] == '.')) {
                dp[i][j] = true;
            }
        }
    }
    return dp[s.size()][p.size()];
}

不得不说这个最优子结构是十分精妙的,很少的判断,很少的步骤,就能涵盖所有的情况。

代码的时间复杂度为 O(n2)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值