【icyle】Leetcode-cn:10. 正则表达式匹配

题目

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

- ‘.’ 匹配任意单个字符

- ‘*’ 匹配零个或多个前面的那一个元素

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

示例 1:

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

示例 2:

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

示例 3:

输入:s = “ab” p = “."
输出:true
解释:".
” 表示可匹配零个或多个(’*’)任意字符(’.’)。

示例 4:

输入:s = “aab” p = “cab”
输出:true
解释:因为 ‘*’ 表示零个或多个,这里 ‘c’ 为 0 个, ‘a’ 被重复一次。因此可以匹配字符串 “aab”。

示例 5:

输入:s = “mississippi” p = “misisp*.”
输出:false

解答

思路

动态规划。
代码中注释已经将细节讲的非常清楚,此处不再赘述。

需要的头文件

string、vector

代码
/*
 * @lc app=leetcode.cn id=10 lang=cpp
 *
 * [10] 正则表达式匹配
 */

// @lc code=start
#include <vector>
#include <string>
using namespace std;
class Solution
{
public:
    bool isMatch(string s, string p)
    {
        int slength = s.size();
        int plength = p.size();
        //定义动态规划矩阵,注意这里我们矩阵也是用数学上的计算,所以下标应该再多一个
        vector<vector<bool>> dp(slength + 1);
        for (int i = 0; i < dp.size(); i++)
        {
            dp[i].resize(plength + 1);
        }

        // 先填上可以填的
        // 如果i,j都是0,那么两个空字符串一定匹配
        // 如果i是0,j是1,那么非空字符串(且第一位一定不是*)一定不匹配空字符串

        dp[0][0] = true;
        dp[0][1] = false;

        //i=0的一行是有规律的,因为是非空字符串匹配空字符串,
        //只有x*这样的子串才能自己消掉自己,这个字串长度是2,
        //所以结果一定与dp[i][j-2]匹配
        for (int j = 2; j <= plength; j++)
        {
            dp[0][j] = dp[0][j - 2] && p[j - 1] == '*';
        }

        // 如果i≠0,j是0,那么空字符串一定不匹配不为空的字符串
        for (int i = 1; i <= slength; i++)
        {
            dp[i][0] = false;
        }

        //i=0行和j=0列都填完了,这里要开始按方法填表
        //!!!注意,这里矩阵是数学形式,而字符串还是用的下标
        //!!!所以,举个例,当dp的i取1的时候,s[i]应该取0,j同理
        for (int i = 1; i <= slength; i++)
        {
            for (int j = 1; j <= plength; j++)
            {
                //1.匹配情况:普通字母匹配相等,或者是通配符.号,就看前面的那段是否匹配
                if (p[j - 1] == s[i - 1] || p[j - 1] == '.')
                {
                    dp[i][j] = dp[i - 1][j - 1];
                }
                //2.*号情况:分类讨论
                else if (p[j - 1] == '*')
                {
                    //假如字符串p是ab*,对字符串s进行匹配,s的情况如下
                    //3.1 s是a,那么ab*应该减去2个字符进行匹配(b*匹配0个b,自己消掉自己),这是dp[i][j - 2]的情况
                    //3.2 s是ab,那么ab*应该减去1个字符进行匹配(b*匹配1个b,把*号删掉就可以了),这是dp[i][j - 1]的情况
                    //3.3 s是abb或者abbb等,那么这类可以看作ab*与a(n个b,n>=2)进行匹配
                    //我们应该参考ab*与a(n-1个b)的匹配结果,毕竟后面多一个b是不影响*号的多个作用的
                    //比如ab*与abb,就应该参考ab*与ab是否匹配。所以看出规律,是dp[i][j]=dp[i-1][j]
                    //上面三种情况综合考虑,有一种为true,这次匹配就可以判true
                    //注意,这里也要分前面的b是否匹配以及通配符的情况

                    if (p[j - 2] == s[i - 1] || p[j - 2] == '.') //x*的x匹配上了,或者是通配符加星号 .* 形式
                    {
                        //注意,即使匹配上了,也要看dp[i][j - 2]
                        //因为对于a和aa*的这种情况,由于字母重复导致误认为要用到后面这组“a*”进行匹配,实际上归到没匹配上的情况里去
                        dp[i][j] = dp[i][j - 2] || dp[i][j - 1] || dp[i - 1][j];
                    }
                    else //证明这次的x*的x没匹配上,可以丢掉
                    {
                        dp[i][j] = dp[i][j - 2];
                    }
                }
                //3.不匹配情况:普通字母匹配且不相等,直接返回false
                //这个一定要摆在最后面!否则*号判断会被这个覆盖
                else
                {
                    dp[i][j] = false;
                }
            }
        }
        return dp[slength][plength];
    }
};
// @lc code=end

时间复杂度和空间复杂度
  • 时间复杂度:已知m 和 n 分别是字符串 s 和 p 的长度。因为对每一个s[i]和p[j](0<=i<=m,0<j<=n),都要进行一次计算状态,所以为 O(mn)
  • 空间复杂度:建立了一个vector型二维数组存放对应位置的状态,所以为 O(mn)

反思与总结

比较困难的一道题,着重反思和总结的点就是想明白动态规划怎么做。多看看代码中的注释吧!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值