LeetCode week 11 : Regular Expression Matching

题目

地址: https://leetcode.com/problems/regular-expression-matching/description/
类别: Dynamic Programming
难度: Hard
描述:

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

分析

给定两个字符串s和p,字符串中包括字母以及两个符号‘.’和‘*’,判断它们是否正则匹配,其规则如下:

  • ‘ . ’ 能匹配单个任意字母
  • ‘ * ’ 表示它前面的字母或‘ . ’ 能出现零次或者多次(注意‘ * ’前一个字符不能为‘ * ’且它前面必须有字母或者‘ . ’)
  • 若两个字符串能通过上面两条解释方法使解释后的字符串完全相同,则称这两个字符串匹配。

例:
s = “aab”, p = “c*a*b”
对p进行解析:第一个‘ * ’使c出现0次,第二个‘ * ’使a出现两次,则可将p解释为”aab“,与s相同,因此两者匹配。

思路

  • 方法:动态规划
  • 目标:判断两个字符串s[0…m-1]和p[0…n-1]是否正则匹配。
  • 子问题f(i,j):若s的前缀s[0…i-1]和p的前缀p[0…j-1]匹配,即s的前i个字符和p的前j个字符正则匹配,则f(i,j)= true,否则为false。
  • 最终目标:f(m,n)。
  • 状态转移方程:
    对p[j-1]进行分类讨论

    1.  If p[j-1] == s[i-1] :  f[i][j] = f[i-1][j-1];
    2.  If p[j-1] == '.' : f[i][j] = f[i-1][j-1];
    3.  If p[j-1] == '*'(则p[j-2]为字母或‘ . ’): 
         --if (p[j-2] == s[i-1] || p[j-2] == '.' || s[i-1] == '.')(即两者等价): 
               f[i][j] =  f[i][j-2] (“p[j-2]*”解释为空)
                       || f[i][j-1] (“p[j-2]*”解释为一个s[i-1])
                       || f[i-1][j] (“p[j-2]*”解释为多个s[i-1])
    
         --else (p[j-2] , s[i-1]两者不等价)
               f[i][j] = f[i][j-2]  (“p[j-2]*”解释为空)
    
  • 时间复杂度:O(mn)

  • 空间复杂度:O(mn)

  • 代码实现

class Solution {
public:
    bool isMatch(string s, string p) {
        int length1 = s.length(), length2 = p.length();
        vector<vector<bool>> f(length1+1, vector<bool>(length2+1, false));
        f[0][0] = true;
        //s[0...i-1]与空字符串匹配当且仅当s[i-1]为*,且s[0...i-3]与空字符串匹配
        for (int i = 1; i < length1+1; i++)
            f[i][0] = i > 1 && '*' == s[i-1] && f[i-2][0];
        //与s类似
        for (int j = 1; j < length2+1; j++)
            f[0][j] = j > 1 && '*' == p[j-1] && f[0][j-2];
        for(int i = 1; i < length1+1; i++) {
            for (int j = 1; j < length2+1; j++)
            {
                if(p[j-1] == s[i-1])    f[i][j] = f[i-1][j-1];
                if(p[j-1] == '.')       f[i][j] = f[i-1][j-1];
                if(p[j-1] == '*') {
                    if(p[j-2] == s[i-1] || p[j-2] == '.' || s[i-1] == '.')  f[i][j] = f[i][j-2] || f[i][j-1] || f[i-1][j];
                    else f[i][j] = f[i][j-2];
                }
            }
        }
        return f[length1][length2];
    }
};
  • 简化
    一开始没注意到s字符串其实是没有‘*’和‘.’的,就是说s全是字母,只需要解释p即可,则原代码可简化如下:
class Solution {
public:
    bool isMatch(string s, string p) {
        int length1 = s.length(), length2 = p.length();
        vector<vector<bool>> f(length1+1, vector<bool>(length2+1, false));
        f[0][0] = true;
        for (int i = 1; i < length1+1; i++)
            f[i][0] = false;
        for (int j = 1; j < length2+1; j++)
            f[0][j] = j > 1 && '*' == p[j-1] && f[0][j-2];
        for(int i = 1; i < length1+1; i++) {
            for (int j = 1; j < length2+1; j++) {
                if(p[j-1] == '*') {
                    if(p[j-2] == s[i-1] || p[j-2] == '.')  f[i][j] = f[i][j-2] || f[i][j-1] || f[i-1][j];
                    else f[i][j] = f[i][j-2];
                }
                if(p[j-1] != '*') {
                    if(p[j-1] == s[i-1] || p[j-1] == '.')  f[i][j] = f[i-1][j-1];
                    else f[i][j] = false;
                }
            }
        }
        return f[length1][length2];
    }
};
  • Debug
    在一开始的时候将 f[i][0] = i > 1 && ’ * ‘== s[i-1] && f[i-2][0];这句中的s误写成了p,导致在遇到s = “ba”, p = “”时在本地测试时正确,而在网上提交得到错误答案。主要原因还是数组越界时本地得到的值与网上多用例测试得到’ * ‘== s[i-1]的结果不同,本地因为只有一个测试用例,所以即使越界也不会导致s[i-1]地址内的元素是’ * ‘,而多用例测试时则会刚好越界地址使表达式为真从而使错误显现。另外还需注意的一点是leetcode在进行测试时是多用例一次性测试完的,所以最后不要使用静态变量。

  • 递归解法:https://discuss.leetcode.com/topic/6183/my-concise-recursive-and-dp-solutions-with-full-explanation-in-c

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值