LeetCode 第10题:正则表达式匹配

题目描述

给定一个字符串s和一个字符规律p,请你来实现一个支持'.'和'*'的正则表达式匹配。

        '.'匹配任意单个字符

        '*'匹配零个或多个前面的那一个元素

所谓匹配,是要涵盖整个字符串s的,而不是部分字符串。

难度:困难

题目链接10. 正则表达式匹配 - 力扣(LeetCode)

示例1:

输入:s="aa",p="a"
输出:false
解释:"a"无法匹配"aa"整个字符串

示例2:

输入:s="aa",p="a*"
输出:true
解释:因为'*'代表可以匹配零个或多个前面的那一个元素,在这里前面的元素就是'a'。因此,字符串"aa"可被视为'a'重复了一次。

示例3:

输入:s="ab",p=".*"
输出:true
解释:".*"表示可匹配零个或多个('*')任意字符('.'),'.'匹配a,然后'*'先匹配'.',然后再转换为b

提示

  • 1<=s.length<=20
  • 1<=p.length<=20
  • s只包含从a-z的小写字母
  • p只包含从a-z的小写字母,以及字符.和*
  • 保证每次出现字符*时,前面都匹配到有效的字符

解题思路:动态规划

  • 从左往右扫的话,字符后面是否跟着星号会影响结果,分析起来有点复杂。

        例如:s="aab",p="c*a*b"

        是否会思考,a与c不匹配,故直接输出false。

        一定要考虑后面是否还跟着'*'

        c*匹配的可以是零个c,这样s和p就完全匹配了,最后将输出true

  • 从右往左扫描,星号的前面肯定有一个字符,星号也只影响这一个字符,它就像一个拷贝器。

        例如:s="aab",p="c*a*b"

        b和b对应,然后a*和aa对应,最后c*和' '对应。

        s和p串是否匹配,取决于:最右端是否匹配、剩余的子串是否匹配。

        只是最右端可能是特殊符号,需要分情况讨论。

案例分析

  1. p为空串,s不为空串,肯定不匹配
  2. s为空串,但p不为空串,要想匹配,只可能是右端是星号,它干掉一个字符后,将p变为空串。
  3. s、p都为空串,肯定匹配。
  4. s、p都不为空串,从右侧开始,依次对比右侧子串和剩余子串是否都匹配。

定义f[i][j]表示s的前i个字符和p的前j个字符是否能够匹配。

  • 如果p的第j个字符是一个小写字母,那么我们必须在s中匹配一个相同的小写字母,即:                 f[i][j]=\left\{\begin{matrix} f[i-1][j-1], &s[i]=p[j] \\ false& s[i]\neq p[j] \end{matrix}\right.
  • 如果p的第j个字符是*,那么就表示我们可以对p的第j-1个字符匹配任意自然数次。根据上文所述,字母+星号的组合在匹配的过程中,本质上只会存在两种情况:
  1. 匹配s末尾的一个字符,将该字符去除,而该组合还可以继续进行匹配。
  2. 不匹配字符,将该组合去除,不再进行匹配。根据上述分析,可以写出精巧的状态转移方程:

f[i][j]=\left\{\begin{matrix} f[i-1][j] or f[i][j-2], & s[i]=p[j-1]\\ f[i][j-2]& s[i]\neq p[j-1] \end{matrix}\right.

  • 在任意情况下,只要p[j]是'.',那么p[j]一定匹配s中的任意一个小写字母。最终的状态转移方程如下:

f[i][j]=\left\{\begin{matrix} if(p[j]\neq '*')=\left\{\begin{matrix} f[i-1][j-1], &matches(s[i],p[j]) \\ false,& otherwise \end{matrix}\right.\\ otherwise=\left\{\begin{matrix} f[i-1][j]orf[i][j-2], &matches(s[i],p[j-1]) \\ f[i][j-2]& otherwise \end{matrix}\right. & \end{matrix}\right.

注意:

动态规划的边界条件为f[0][0]=true,即两个空字符串是可以匹配的。最终的答案即为f[m][n],其中m和n分别代表的是s和p的长度。由于大部分语言中,字符串的字符下标是从0开始的,因此在实现上面的状态转移方程时,需要注意状态中每一维下标与实际字符下标的对应关系。

class Solution{

public: 
    bool isMatch(string s,string p){
    int m=s.size(),n=p.size();//m和n分别代表字符串s和p的长度
    
    auto matches = [&](int i,int j)
    {
        if(i==0)
            return false;
        if(p[j-1]=='.')
            return true;
        return s[i-1]==p[j-1];
    }
    vector<vector<int>> f(m+1,vector<int>(n+1));//定义f[m+1][n+1]
    f[0][0]=true;//定义初始值为true或1
    for(int i=0;i<=m;i++)
    {
        for(int j=0;j<=n;j++)
        {
            if(p[j-1]=='*')
                f[i][j] |= f[i][j-2];
                if(matches(i,j-1))//p[j-1]=='.'
                    f[i][j] |= f[i-1][j];
            else
                if(matches(i,j))
                    f[i][j] |= f[i-1][j-1];
                
         }
    }

    return f[m][n];


}
};

复杂度分析: 

时间复杂度:O(mn),其中m和n分别是字符串s和p的长度。

空间复杂度:O(mn),即为存储所有状态使用的空间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值