leetcode每日一题--10. 正则表达式匹配

  1. 题目描述
  2. 题解
  3. 代码和优质代码
  4. 闲话

题目描述

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

‘.’ 匹配任意单个字符
‘*’ 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。

说明:

s 可能为空,且只包含从 a-z 的小写字母。 p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/regular-expression-matching
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

实例

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

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

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/regular-expression-matching
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解

这次好久没写了,主要是最近在复习高数呢!
这次我想聊一聊动态规划,你面试可能会用到的.(我猜的),我对动态规划了解归了解但是不太熟.这次就好好剖析一下这个所谓的动态规划!

这次题呢用到了动态规划,什么是动态规划,我的理解就是思路到位,计算机给你全部安排好.
我们来好好看看这个题目是怎么样规划的!

先对题目要有大致理解,不要随便的就理解就开始做题了.
如果你了解正则表达式,应该对.和*的作用明白(不知道也没有关系,但请你好好读第二个引用部分)

动态规划最难得部分来了
写出转移方程
怎么考虑出状态转移方程?
dp[i][j]代表此时前面都匹配成功,不知道接下来的情况.
s[i]代表目标字符串
p[j]需要匹配的字符串的表达式
匹配情况分为三种:

  1. p[j]== s[i] 那么 dp[i][j] =dp[i-1][j-1],因为情况与前面的相同
  2. p[j] == ‘.’, 那么他可以匹配任何东西,所以p[j] == s[i]是必然的 ,所以dp[i][j] =dp[i-1][j-1]成立
  3. p[j]==’*’,这就有点复杂了.

前面两种情况很简单,第三种就需要细细想想了.
由于*可以0或者多个匹配,所以分两种情况去考虑

  1. 当*时0匹配是什么情况?p[j-1]! = s[i] && p[j-1]!=’.'的时候所以此时的 dp[i][j] = dp[i][j-2],此时你是0个匹配所以你要字符串p往前找两个字符的情况,与之相等.

举个例子:s字符串为"aab" p字符串为"aac*b",你可以看出,当你将 * 作为0个匹配c,那么p字符串就可以当成"aab"与s字符串匹配成功,因为’c’与’b’是不相等的,所以字符串p还往前找一个字符的匹配情况,因为是0匹配,所以,你可以当成不存在,所以此时匹配情况和前两个字符匹配情况是一样的.

我们再举一个例子:用s = “aaab”, p = “ac * a * b”, 第一个 * 是0匹配,第二个 * 是重复一次匹配,那么p可以当成"aab"与s进行匹配成功了.解释与上一个是一样的,‘a’!=‘c’,所以你要字符串p往前面找一个字符的匹配情况,因为这个* 是0匹配,所以,'c’你可以当做不存在.此时匹配情况和前两个字符匹配情况是一样的.

第一种情况在两个例子下应该懂了吧,我们看看多次匹配的情况!

  1. 当*时一次匹配时候,这个就很简单就是字符串p往前找一个字符的匹配情况,与之相等dp[i][j] = dp[i][j-1].
  2. 当*时多次进行匹配时候,他就不是在p上往前找多个字符了,而是直接找s上的前一个.这就要看你对dp这个二维数组是怎么理解的,dp[i][j]指的是s字符串的第i个字符,在p的j个字符能否匹配成功.当你要多次匹配,你没有必要去在字符串p往前找多少多少个字符,而是直接找s字符串的前一个字符,如果前面都匹配成功的话,那么你重复匹配也会成功.所以dp[i][j] = dp[i-1][j].
  3. 其实还有有一种情况挺难思考出来,我也在一直找错中,其实当p[j-1]=’.'的时候他是可以等于任何一个的,那么就会存在一种情况,那么就是0匹配或者是多次匹配.其实这种情况很难把握,既然’.’ 可以代表任何字符,你就可以当做确认的字符来对待,所以上面一种情况就是这个情况的子集了.例如s字符串为"aaab" p字符串为"aaab.*",你看的出来最后两个字符没有意义的.所以这个情况也要考虑到.匹配的情况和之前的0匹配是一样的.

总结一下:

  1. 当p[j]== s[i],那么匹配情况就是dp[i][j] =dp[i-1][j-1].
  2. p[j] == '.'匹配情况就是dp[i][j] =dp[i-1][j-1].
  3. p[j]==’*‘匹配情况就是
    (1)p[j-1]! = s[i] && p[j-1]!=’.’ 匹配情况就是dp[i][j] = dp[i][j-2]
    (2)p[j-1] = =s[i]匹配情况就是dp[i][j] = dp[i][j-1] || dp[i][j] = dp[i-1][j],满足一种情况就可
    (3)p[j-1] ==’.’ 匹配情况就是dp[i][j] = dp[i][j-1] || dp[i][j] = dp[i-1][j] || dp[i][j] = dp[i][j-2],满足任何一种即可.其实第二种可以看做第三种的子集所以你可以不用写第二种但是要考虑到.

基本就这样吧!!!我是这样理解的,如果有什么疑问,可以评论提出.

代码

class Solution {
public:
    bool isMatch(string s, string p) {
        bool dp[s.length()+1][p.length()+1]={false};//c++这里要默认初始化不然编译环境不同可能会出问题比如:VS默认true{xxx}。  
	    dp[0][0]=true;//dp[i][j] 表示 s 的前 i 个是否能被 p 的前 j 个匹配
        //对p情况的a*b*c*匹配空的s  进行预处理,并且初始化
	    for(int i=0;i<p.length();i++){
            //这里疑问点是下标问题,首先p第一个字母下标从0开始,dp这里指的是字符串前n个字符
            //所以这里p的第k个字符串是否匹配对应dp里面[0][k+1]
            //其次这里只处理p为a*b*c*,s为空的情况所以条件dp[0][i-1]是看前i-1字符串是否匹配 `your inline code...` 
            if(p[i]=='*' &&dp[0][i-1])
                dp[0][i+1]=true;
            else
                dp[0][i+1]=false;//加这句话补充初始化数组。
        }
        for(int i=0;i<s.length();i++){
            for(int j=0;j<p.length();j++){
                //对以下两种情况匹配
                //如果 p.charAt(j) == s.charAt(i) : dp[i][j] = dp[i-1][j-1];
                //如果 p.charAt(j) == '.' : dp[i][j] = dp[i-1][j-1];
                //同理这里有人可能会疑惑dp[i+1][j+1] = dp[i][j];
                //简单来说p,s字符串首字母从0开始的
                //dp的意思是表示 s 的前 i 个是否能被 p 的前 j 个匹配
                //p[k]表示p中第k+1个字符,换到dp中即为k+1
                if(p[j]=='.' || p[j]==s[i]){
                    dp[i+1][j+1] = dp[i][j];
                }
                if(p[j] == '*'){
                    //如果 p.charAt(j-1) != s.charAt(i) : dp[i][j] = dp[i][j-2] 
                    //in this case, a* only counts as empty
                    // 如果 p.charAt(i-1) == s.charAt(i) or p.charAt(i-1) == '.':
                    // dp[i][j] = dp[i-1][j] //in this case, a* counts as multiple a
                    // or dp[i][j] = dp[i][j-1] // in this case, a* counts as single a
                    // or dp[i][j] = dp[i][j-2] // in this case, a* counts as empty
                    if(p[j-1] != s[i]&&p[j-1]!='.')
                        dp[i+1][j+1] = dp[i+1][j-1];
                    else
                        dp[i+1][j+1]=(dp[i + 1][j] || dp[i][j + 1] || dp[i + 1][j - 1]);
                }
            }
        }
	    return dp[s.length()][p.length()];
    }
};

我懒得的写注释,所以抄了一下别人写好的代码,其实考虑的方向是一样的.

这也有别人写的特别好的题解可以多看看.

时间比较短的代码

class Solution {
public:
    vector<vector<int>> res;
    string s,p;
    bool isMatch(string _s, string _p) {
        s=_s;
        p=_p;
        res=vector(s.size()+1,vector(p.size()+1,-1));
        return Best(0,0);
    }
    bool Best(int i,int j){
        if(res[i][j]!=-1) return res[i][j];
        if(j==p.size()) return res[i][j]=i==s.size();
        bool fm=i<s.size()&&(s[i]==p[j]||p[j]=='.');
        bool ans;
        if(j+1<p.size()&&p[j+1]=='*'){
            ans=Best(i,j+2)||fm&&Best(i+1,j);
        }
        else{
            ans=fm&&Best(i+1,j+1);
        }
        return res[i][j]=ans;
    }
};

多学习是我们的代言.
我感觉自己讲的应该清晰吧!!如果有任何问题可以评论找我.

闲话

最近买了一本游戏编程入门,接下几个月有的玩了.哈哈哈哈!!

你们好!我是大一小菜鸡,又菜瘾还大

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值