LeetCode高频题10:正则表达式匹配

LeetCode高频题10:正则表达式匹配

提示:本题是系列LeetCode的150道高频题,你未来遇到的互联网大厂的笔试和面试考题,基本都是从这上面改编而来的题目
互联网大厂们在公司养了一大批ACM竞赛的大佬们,吃完饭就是设计考题,然后去考应聘人员,你要做的就是学基础树结构与算法,然后打通任督二脉,以应对波云诡谲的大厂笔试面试题!
你要是不扎实学习数据结构与算法,好好动手手撕代码,锻炼解题能力,你可能会在笔试面试过程中,连题目都看不懂!比如华为,字节啥的,足够让你读不懂题
在这里插入图片描述
这就是网易的招聘面试题,你得有本事才能拿下,现场手撕代码,写出来才能进去赚钱,拿高薪!
如果拿不下这类的题目,不好意思,网易不会招聘你的,就是这么现实!


文章目录

  • LeetCode高频题10:正则表达式匹配
    • @[TOC](文章目录)
  • 题目
  • 一、审题
  • 二、解题
  • 俩字符串:必然是考虑一个样本做行,一个样本做列,动态规划模型DP3:样本位置对应模型
    • 查s和p是否有效呢?
    • 首先尝试暴力递归
    • f(str,pattern,si,pi)内部怎么写代码?
  • 笔试面试AC解:根据暴力递归改傻缓存,记忆化搜索法填dp表
  • 总结

题目

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

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

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


一、审题

示例 1:

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

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

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

提示:

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


二、解题

‘.’ 匹配任意单个字符,点可以单独出现
‘*’ 匹配零个或多个前面的那一个元素,星必须配合前面一个字符使用,不能单独出现

s只包含小写字母
p可能有小写,可能有点,可能有星
LeetCode高频题10:正则表达式匹配_第1张图片
比如:
LeetCode高频题10:正则表达式匹配_第2张图片
d*可以变为三个d
星配合前面的字符使用,变任意0–N个d来
LeetCode高频题10:正则表达式匹配_第3张图片
一个点,可以变一个字符
LeetCode高频题10:正则表达式匹配_第4张图片
点和星配合使用非常强大,可以变任意个点出来,然后由点去变任意字符,非常强大!!!
LeetCode高频题10:正则表达式匹配_第5张图片
匹配失败的情况:
LeetCode高频题10:正则表达式匹配_第6张图片
点一定要配出来一个字符哦!!!
LeetCode高频题10:正则表达式匹配_第7张图片
不过出现强大的点星配合就是可以,让点星变0个点完事了
LeetCode高频题10:正则表达式匹配_第8张图片
本题是真的挺复杂的

俩字符串:必然是考虑一个样本做行,一个样本做列,动态规划模型DP3:样本位置对应模型

之前咱们老说的四种互联网大厂必考的动态规划模型:
【1】动态规划4种模型:暴力递归的尝试原则,如何到傻缓存dp表跟随(记忆化搜索方法),到精细化改动态规划转移方程填dp表

互联网大厂的动态规划题目中的四种经典暴力递归尝试模型:
(1)DP1:从左往右的尝试模型,关注i位置结尾,或者i位置开头的情况,或者看i联合i+1,i+2的情况,填表往往是上到下,或者下到上,左到右,右到左。
(2)DP2:从L–R范围上的尝试模型,关注L和R的情况,填表格式非常固定,主对角,副对角,倒回来填
(3)DP3:多样本位置对应的尝试模型,2个样本,一个样本做行,一个样本做列,关注i和j对应位置的情况,先填边界,再填中间
(4)DP4:业务限制类的尝试模型,比如走棋盘,固定的几个方向可以走,先填边界,再填中间。

本题就是DP3:2个样本,一个样本做行,一个样本做列的样本位置对应模型,咱们慢慢尝试暴力递归,然后再改为动态规划的代码

查s和p是否有效呢?

s和p不能出问题
(1)s不能有.和星
(2)p字符串如果出现星,前面必须要有一个字符串,所以星不能出现在首位置,出现星,前面就必须是a–z,决不能出现星星

手撕过滤代码:

        //复习
        //检查s和p的有效性
        public static boolean isValid(char[] str, char[] pattern){
            //查s和p是否有效呢?
            //(1)s不能有.和星
            for(char c:str){
                if (c == '.' || c == '*') return false;
            }
            //(2)p字符串如果出现星,前面必须要有一个字符串,所以星不能出现在首位置,
            // 而且出现星,前面就必须是a--z,决不能出现星星
            for (int i = 0; i < pattern.length; i++) {
                if (pattern[i] == '*' && (i == 0 || pattern[i - 1] == '*') ) return false;
            }
            return true;
        }

当然,题目里面说了
s 只包含从 a-z 的小写字母。
p 只包含从 a-z 的小写字母,以及字符 . 和 *。
保证每次出现字符 * 时,前面都匹配到有效的字符
上面的(1)是没意义的,不过没关系,咱们自己也可以过滤的


首先尝试暴力递归

定义暴力递归函数:f(str,pattern,si,pi)含义是
str从si–N-1范围内的字符串,能否由pattern从pi–M-1范围内的字符串匹配(变)出来?
能为true,否为false

在代码中咱们这么定义,具体咋做待会说:

//定义的f:str从si--N-1范围内的字符串,能否由pattern从pi--M-1范围内的字符串匹配(变)出来?
        public static boolean f(char[] str, char[] pattern, int si, int pi){
            
        }

主函数直接调用:
f(s,p,0,0)
意思是:
s从0–N-1范围内的字符串,能否由p从0–M-1范围内的字符串匹配(变)出来?
就是s是否能被pattern变出来
就是题目的答案

先检查有效性,不合法的s和p没必要判断,必然不可以匹配
然后就用f去判断了

        //s从0--N-1范围内的字符串,能否由p从0--M-1范围内的字符串匹配(变)出来?
        //主函数
        public boolean isMatch(String s, String p) {
        if (s == null || p == null) return false;//null没法整
            if (p.isEmpty()) return s.isEmpty();//两者都为空,可以

            char[] str = s.toCharArray();
            char[] pattern = p.toCharArray();
            if (!isValid(str, pattern)) return false;
            
            return f(str, pattern, 0, 0);//样本位置对应模型
        }

下面咱们就就根据题目要求,看看怎么能把f整出来
真的调用f时,p在pi处,属于开头,p的开头决不能是星,这是基本原则!!!
LeetCode高频题10:正则表达式匹配_第9张图片

f(str,pattern,si,pi)内部怎么写代码?

(1)基础情况就是当si都走到了str的N位置,越界了,则s的N–N-1就是一个空串""
这个时候,pattern的pi–M-1咋着才能匹配出空串呢?
要么pi也是到了pattern的末尾M位置,M–M-1也是空串""
LeetCode高频题10:正则表达式匹配_第10张图片

要么pi出有字符,但是从j=pi+1开始,j往后每次递增2,知道M-1全部都是星,这样保证x星y星z星等等这种模型出现
才能让x星变0个x,就是空串"",这样才能匹配
LeetCode高频题10:正则表达式匹配_第11张图片
如果上面两种情况都不行
pi是x,但是pi+1位置没法出星,那不好意思,完蛋
LeetCode高频题10:正则表达式匹配_第12张图片
手撕代码:

//(1)基础情况就是当si都走到了str的N位置,越界了,则s的N--N-1就是一个空串""
            if (si == str.length){
                //这个时候,pattern的pi--M-1咋着才能匹配出空串呢?
                //要么pi也是到了pattern的末尾M位置,M--M-1也是空串""
                if (pi == pattern.length) return true;
                //要么pi出有字符,但是从j=pi+1开始,j往后每次递增2,知道M-1全部都是星,这样保证x星y星z星等等这种模型出现
                //才能让x星变0个x,就是空串"",这样才能匹配
                if (pi + 1 < pattern.length && pattern[pi + 1] == '*')
                    return f(str, pattern, si, pi + 2);//j每次递增2都得是
                //
            }

(2)pi来到了pattern的末尾M,str的si–N-1范围内得是啥情况呢?才能匹配呢
这是pattern是空串"",此时str的si–N-1,必须是空串才行
因为pattern的空串,是没有办法变出字符来的……
LeetCode高频题10:正则表达式匹配_第13张图片

//(2)pi来到了pattern的末尾M,str的si--N-1范围内得是啥情况呢?才能匹配呢
            //这是pattern是空串"",此时str的si--N-1,必须是空串才行
            if (pi == pattern.length) return si == str.length;

最基本的极端条件就考虑完成

(3)接下来,si和pi 都没有到终止位置,怎么匹配出来呢,就看pattern中点与星的状况了

1)如果pi+1位置不是星,意味着pi必须独立匹配上si那个字符
注意:如果pi+1越界了,其实也是pi+1那不是星,
或者pi没越界的话
pi可以是点,或者pi就是si那个字符,这两种情况才能匹配上,
当然pi不会是星,开头位置不会是星的哦
LeetCode高频题10:正则表达式匹配_第14张图片
如果pi+1处是点,你还得保证str的si+1之后与pi+1之后能配上【不能只看si和pi呗】
比如:
LeetCode高频题10:正则表达式匹配_第15张图片
LeetCode高频题10:正则表达式匹配_第16张图片
LeetCode高频题10:正则表达式匹配_第17张图片

总之,你就是要保证,在pi+1越界,或者pi+1就不是星的情况下
pi独自面对si时,最好让他俩匹配,
还要同时保证后面si+1后续的所有字符串也都要与pi+1之后的字符串匹配上,才算是整个串匹配

            //(3)接下来,si和pi **都没有**到终止位置,怎么匹配出来呢,就看pattern中点与星的状况了
            //1)如果pi+1位置不是星,意味着pi必须独立匹配上si那个字符
            //注意:如果pi+1越界了,其实也是pi+1那不是星,
            //pi可以是点,或者pi就是si那个字符,这两种情况才能匹配上,pi不会是星,开头位置不会是星的哦
            //如果pi+1处是点,你还得保证str的si+1之后与pi+1之后能配上【不能只看si和pi呗】
            if (pi + 1 >= pattern.length || pattern[pi + 1] != '*')
                return (pattern[pi] == str[si] || pattern[pi] == '.') && 
                        f(str, pattern, si + 1, pi + 1);

是不挺复杂的?这是pi+1不是星的情况

2)pi+1不越界,而且pi+1就是星,又如何?
如果si不等于pi,则你得让pi与pi+1的x星,变没了,
然后保证pi+2往后一串跟现在的si往后一串匹配上才行
LeetCode高频题10:正则表达式匹配_第18张图片

3)再往下pi和si一定能配上了,因为pi可能是点,可能与si相同,而且pi+1就是星

可能pi与pi+1俩x星,需要变0个si出来,如下图,本身si和pi+2后面就相等的
LeetCode高频题10:正则表达式匹配_第19张图片
可能需要可能pi与pi+1俩x星,变1个si出来,如下图,本身si+1和pi+2后面就相等的
LeetCode高频题10:正则表达式匹配_第20张图片
可能需要可能pi与pi+1俩x星,变2个si出来,如下图,本身si+2和pi+2后面就相等的
LeetCode高频题10:正则表达式匹配_第21张图片
可能需要可能pi与pi+1俩x星,变3个si出来,如下图,本身si+3和pi+2后面就相等的
LeetCode高频题10:正则表达式匹配_第22张图片
上面不断去变,有一个成功,就算能匹配上

这个过程,用while循环控制si++来变

看代码:

            //3)再往下pi和si一定能配上了,因为pi可能是点,可能与si相同,而且pi+1就是星
            //可能pi与pi+1俩x星,需要变0个si出来,如下图,本身si和pi+2后面就相等的
            if (f(str, pattern, si, pi + 2)) return true;//跟上面2)一样
            //可能pi与pi+1俩x星,需要变1--N个si出来,如下图,本身si和pi+2后面就相等的
            //这得看si相同的字符出现了几个?前缀
            while(si < str.length && (pattern[pi] == '.' || pattern[pi] == str[si])){
                //只要是pi为点,或者pi与si相同,一定能配上,先让x星变1个si出来
                if (f(str, pattern, si + 1, pi + 2)) return true;
                si++;//si跟着动,pi不动,继续,就是看看让x星变2 3 N个si出来,与pi+2之后匹配
            }//整体就是x星变0 1 2  N个si出来,有其一就OK

先默认可以变0个si,操作一下尝试是否有效?

然后看while循环,si不越界,尝试让x星变1个si,2个si……
第一次si对准pi,看红色si那,x星变1个si出来,然后看看si+1后面与pi+2后面能否匹配上?
第二次,si++,绿色那个si,x星变出2个原来那个a来,看看si+1与pi+2后面能否匹配上?
……
LeetCode高频题10:正则表达式匹配_第23张图片
整体就是x星变0 1 2 N个si出来,有其一就OK
这个代码确实需要功底,复杂,而且难整,需要你考虑周到,否则没法成功的

如果啥时候si越界,或者si压根与pi不等,或者pi不是点了,那while整不动了,pi那x星是不可能变别的字符的,比如上面的b

4)上面所有的这些过程都走完,我们已经考虑周全了,都没法成功,那不好意思,失败了

            //4)上面所有的这些过程都走完,我们已经考虑周全了,都没法成功,那不好意思,失败了
            return false;

这就是网易的招聘面试题,你得有本事才能拿下,现场手撕代码,写出来才能进去赚钱,拿高薪!
如果拿不下这类的题目,不好意思,网易不会招聘你的,就是这么现实!

综合起来,f的代码:

        //定义的f:str从si--N-1范围内的字符串,能否由pattern从pi--M-1范围内的字符串匹配(变)出来?
        public static boolean f(char[] str, char[] pattern, int si, int pi){
            //(1)基础情况就是当si都走到了str的N位置,越界了,则s的N--N-1就是一个空串""
            if (si == str.length){
                //这个时候,pattern的pi--M-1咋着才能匹配出空串呢?
                //要么pi也是到了pattern的末尾M位置,M--M-1也是空串""
                if (pi == pattern.length) return true;
                //要么pi出有字符,但是从j=pi+1开始,j往后每次递增2,知道M-1全部都是星,这样保证x星y星z星等等这种模型出现
                //才能让x星变0个x,就是空串"",这样才能匹配
                if (pi + 1 < pattern.length && pattern[pi + 1] == '*')
                    return f(str, pattern, si, pi + 2);//j每次递增2都得是
                //pi除了x,但是pi+1位置没法出星,那不好意思,完蛋
                return false;
            }

            //(2)pi来到了pattern的末尾M,str的si--N-1范围内得是啥情况呢?才能匹配呢
            //这是pattern是空串"",此时str的si--N-1,必须是空串才行
            if (pi == pattern.length) return si == str.length;

            //(3)接下来,si和pi **都没有**到终止位置,怎么匹配出来呢,就看pattern中点与星的状况了
            //1)如果pi+1位置不是星,意味着pi必须独立匹配上si那个字符
            //注意:如果pi+1越界了,其实也是pi+1那不是星,
            //pi可以是点,或者pi就是si那个字符,这两种情况才能匹配上,pi不会是星,开头位置不会是星的哦
            //如果pi+1处是点,你还得保证str的si+1之后与pi+1之后能配上【不能只看si和pi呗】
            if (pi + 1 >= pattern.length || pattern[pi + 1] != '*')
                return (pattern[pi] == str[si] || pattern[pi] == '.') &&
                        f(str, pattern, si + 1, pi + 1);

            //2)上面的1)跳过的话,必然此时的pi+1不越界,而且pi+1就是星
            if (pattern[pi] != '.' && pattern[pi] != str[si]){
                //如果si不等于pi,则你得让pi与pi+1的x星,变没了,然后保证pi+2往后一串跟现在的si往后一串匹配上才行
                return f(str, pattern, si, pi + 2);
            }
            //3)再往下pi和si一定能配上了,因为pi可能是点,可能与si相同,而且pi+1就是星
            //可能pi与pi+1俩x星,需要变0个si出来,如下图,本身si和pi+2后面就相等的
            if (f(str, pattern, si, pi + 2)) return true;//跟上面2)一样
            //可能pi与pi+1俩x星,需要变1--N个si出来,如下图,本身si和pi+2后面就相等的
            //这得看si相同的字符出现了几个?前缀
            while(si < str.length && (pattern[pi] == '.' || pattern[pi] == str[si])){
                //只要是pi为点,或者pi与si相同,一定能配上,先让x星变1个si出来
                if (f(str, pattern, si + 1, pi + 2)) return true;
                si++;//si跟着动,pi不动,继续,就是看看让x星变2 3 N个si出来,与pi+2之后匹配
            }//整体就是x星变0 1 2  N个si出来,有其一就OK

            //4)上面所有的这些过程都走完,我们已经考虑周全了,都没法成功,那不好意思,失败了
            return false;
        }

测试一波:

    public static void test(){
        Solution solution = new Solution();
        System.out.println(solution.isMatch("aab", "c*a*b"));
        //"ab"
        //".*"
    }

    public static void main(String[] args) {
        test();
    }

可以:

true

LeetCode测试
LeetCode高频题10:正则表达式匹配_第24张图片
牛逼吧?

但是呢,速度有点慢,当然了………………
暴力递归f肯定速度慢

面试的目的,是需要你优化的,2变量si和pi,样本位置对应模型
是咱们的DP3,完全可以在这暴力递归的基础上改动态规划


笔试面试AC解:根据暴力递归改傻缓存,记忆化搜索法填dp表

f中si和pi就是str遍历的位置,pattern遍历的位置
si从0–N取值,N+1长度
pi从0–M取值,M+1长度
每一个f(char[] str, char[] pattern, int si, int pi)得到的结果,可以放到一个二维表中存起来
存入dp[si][pi],就像下面那个表
LeetCode高频题10:正则表达式匹配_第25张图片
f其实针对每一个si和pi的组合,都会有一个结果的
咱们要的是谁?
f(0,0),即放入dp[0][0],下图五角星那个位置
LeetCode高频题10:正则表达式匹配_第26张图片
最开始dp[i][j]是-1,没有算过
dp如果存0,为false,dp如果存1,则true

没有算过的dpij,遇到return的地方需要重新暴力递归算一下

        //改傻缓存dp
        //定义的f:str从si--N-1范围内的字符串,能否由pattern从pi--M-1范围内的字符串匹配(变)出来?
        public static boolean f2(char[] str, char[] pattern, int si, int pi, int[][] dp){
            //dp算过了,直接拿,速度快
            if (dp[si][pi] != -1) return dp[si][pi] == 0 ? false : true;//这里要转化Boolean
            //下面就是没有求过的,需要填表,返回dp

            //(1)基础情况就是当si都走到了str的N位置,越界了,则s的N--N-1就是一个空串""
            if (si == str.length){
                //这个时候,pattern的pi--M-1咋着才能匹配出空串呢?
                //要么pi也是到了pattern的末尾M位置,M--M-1也是空串""
                if (pi == pattern.length) {
                    dp[si][pi] = 1;
                    return dp[si][pi] == 0 ? false : true;
                }
                //要么pi出有字符,但是从j=pi+1开始,j往后每次递增2,知道M-1全部都是星,这样保证x星y星z星等等这种模型出现
                //才能让x星变0个x,就是空串"",这样才能匹配
                if (pi + 1 < pattern.length && pattern[pi + 1] == '*'){
                    boolean ans = f2(str, pattern, si, pi + 2, dp);//j每次递增2都得是
                    dp[si][pi] = ans ? 1 : 0;
                    return dp[si][pi] == 0 ? false : true;
                }
                //pi除了x,但是pi+1位置没法出星,那不好意思,完蛋
                dp[si][pi] = 0;
                return dp[si][pi] == 0 ? false : true;
            }

            //(2)pi来到了pattern的末尾M,str的si--N-1范围内得是啥情况呢?才能匹配呢
            //这是pattern是空串"",此时str的si--N-1,必须是空串才行
            if (pi == pattern.length) {
                dp[si][pi] = si == str.length ? 1 : 0;
                return dp[si][pi] == 0 ? false : true;
            }

            //(3)接下来,si和pi **都没有**到终止位置,怎么匹配出来呢,就看pattern中点与星的状况了
            //1)如果pi+1位置不是星,意味着pi必须独立匹配上si那个字符
            //注意:如果pi+1越界了,其实也是pi+1那不是星,
            //pi可以是点,或者pi就是si那个字符,这两种情况才能匹配上,pi不会是星,开头位置不会是星的哦
            //如果pi+1处是点,你还得保证str的si+1之后与pi+1之后能配上【不能只看si和pi呗】
            if (pi + 1 >= pattern.length || pattern[pi + 1] != '*') {
                boolean ans = (pattern[pi] == str[si] || pattern[pi] == '.') &&
                        f2(str, pattern, si + 1, pi + 1, dp);
                dp[si][pi] = ans ? 1 : 0;
                return dp[si][pi] == 0 ? false : true;
            }

            //2)上面的1)跳过的话,必然此时的pi+1不越界,而且pi+1就是星
            if (pattern[pi] != '.' && pattern[pi] != str[si]){
                //如果si不等于pi,则你得让pi与pi+1的x星,变没了,然后保证pi+2往后一串跟现在的si往后一串匹配上才行
                boolean ans = f2(str, pattern, si, pi + 2, dp);
                dp[si][pi] = ans ? 1 : 0;
                return dp[si][pi] == 0 ? false : true;
            }
            //3)再往下pi和si一定能配上了,因为pi可能是点,可能与si相同,而且pi+1就是星
            //可能pi与pi+1俩x星,需要变0个si出来,如下图,本身si和pi+2后面就相等的
            if (f2(str, pattern, si, pi + 2, dp)) {
                dp[si][pi] = 1;
                return dp[si][pi] == 0 ? false : true;//跟上面2)一样
            }
            //可能pi与pi+1俩x星,需要变1--N个si出来,如下图,本身si和pi+2后面就相等的
            //这得看si相同的字符出现了几个?前缀
            while(si < str.length && (pattern[pi] == '.' || pattern[pi] == str[si])){
                //只要是pi为点,或者pi与si相同,一定能配上,先让x星变1个si出来
                if (f2(str, pattern, si + 1, pi + 2, dp)) {
                    dp[si][pi] = 1;
                    return dp[si][pi] == 0 ? false : true;
                }
                si++;//si跟着动,pi不动,继续,就是看看让x星变2 3 N个si出来,与pi+2之后匹配
            }//整体就是x星变0 1 2  N个si出来,有其一就OK

            //4)上面所有的这些过程都走完,我们已经考虑周全了,都没法成功,那不好意思,失败了
            dp[si][pi] = 0;
            return dp[si][pi] == 0 ? false : true;
        }

        //s从0--N-1范围内的字符串,能否由p从0--M-1范围内的字符串匹配(变)出来?
        //主函数
        public boolean isMatchDP(String s, String p) {
            if (s == null || p == null) return false;//null没法整
            if (p.isEmpty()) return s.isEmpty();//两者都为空,可以

            char[] str = s.toCharArray();
            char[] pattern = p.toCharArray();
            if (!isValid(str, pattern)) return false;

            //准备dp傻缓存表,f带着填si和pi位置
            int[][] dp = new int[str.length + 1][pattern.length + 1];
            for (int i = 0; i < str.length + 1; i++) {
                for (int j = 0; j < pattern.length + 1; j++) {
                    dp[i][j] = -1;//初始化
                }
            }
            return f2(str, pattern, 0, 0, dp);//样本位置对应模型
        }

然后测试:

    public static void test(){
        Solution solution = new Solution();
        System.out.println(solution.isMatch("aab", "c*a*b"));
        System.out.println(solution.isMatchDP("aab", "c*a*b"));
        //"ab"
        //".*"
    }

    public static void main(String[] args) {
        test();
    }

问题不大

true
true

看看LeetCode反响:
LeetCode高频题10:正则表达式匹配_第27张图片
这个已经是动态规划了,可能这种题目,基本没法节约太多的时间
甭管了
有可能是因为其中某些骚操作常数时间还挺复杂度

再改一下常数时间那些true和false那,这种三目运算判断是真的挺慢的

        //改傻缓存dp
        //定义的f:str从si--N-1范围内的字符串,能否由pattern从pi--M-1范围内的字符串匹配(变)出来?
        public static boolean f2(char[] str, char[] pattern, int si, int pi, int[][] dp){
            //dp算过了,直接拿,速度快
            if (dp[si][pi] != -1) return dp[si][pi] == 0 ? false : true;//这里要转化Boolean
            //下面就是没有求过的,需要填表,返回dp

            //(1)基础情况就是当si都走到了str的N位置,越界了,则s的N--N-1就是一个空串""
            if (si == str.length){
                //这个时候,pattern的pi--M-1咋着才能匹配出空串呢?
                //要么pi也是到了pattern的末尾M位置,M--M-1也是空串""
                if (pi == pattern.length) {
                    dp[si][pi] = 1;
                    return true;
                }
                //要么pi出有字符,但是从j=pi+1开始,j往后每次递增2,知道M-1全部都是星,这样保证x星y星z星等等这种模型出现
                //才能让x星变0个x,就是空串"",这样才能匹配
                if (pi + 1 < pattern.length && pattern[pi + 1] == '*'){
                    boolean ans = f2(str, pattern, si, pi + 2, dp);//j每次递增2都得是
                    dp[si][pi] = ans ? 1 : 0;
                    return dp[si][pi] == 0 ? false : true;
                }
                //pi除了x,但是pi+1位置没法出星,那不好意思,完蛋
                dp[si][pi] = 0;
                return false;
            }

            //(2)pi来到了pattern的末尾M,str的si--N-1范围内得是啥情况呢?才能匹配呢
            //这是pattern是空串"",此时str的si--N-1,必须是空串才行
            if (pi == pattern.length) {
                dp[si][pi] = si == str.length ? 1 : 0;
                return dp[si][pi] == 0 ? false : true;
            }

            //(3)接下来,si和pi **都没有**到终止位置,怎么匹配出来呢,就看pattern中点与星的状况了
            //1)如果pi+1位置不是星,意味着pi必须独立匹配上si那个字符
            //注意:如果pi+1越界了,其实也是pi+1那不是星,
            //pi可以是点,或者pi就是si那个字符,这两种情况才能匹配上,pi不会是星,开头位置不会是星的哦
            //如果pi+1处是点,你还得保证str的si+1之后与pi+1之后能配上【不能只看si和pi呗】
            if (pi + 1 >= pattern.length || pattern[pi + 1] != '*') {
                boolean ans = (pattern[pi] == str[si] || pattern[pi] == '.') &&
                        f2(str, pattern, si + 1, pi + 1, dp);
                dp[si][pi] = ans ? 1 : 0;
                return dp[si][pi] == 0 ? false : true;
            }

            //2)上面的1)跳过的话,必然此时的pi+1不越界,而且pi+1就是星
            if (pattern[pi] != '.' && pattern[pi] != str[si]){
                //如果si不等于pi,则你得让pi与pi+1的x星,变没了,然后保证pi+2往后一串跟现在的si往后一串匹配上才行
                boolean ans = f2(str, pattern, si, pi + 2, dp);
                dp[si][pi] = ans ? 1 : 0;
                return dp[si][pi] == 0 ? false : true;
            }
            //3)再往下pi和si一定能配上了,因为pi可能是点,可能与si相同,而且pi+1就是星
            //可能pi与pi+1俩x星,需要变0个si出来,如下图,本身si和pi+2后面就相等的
            if (f2(str, pattern, si, pi + 2, dp)) {
                dp[si][pi] = 1;
                return true;//跟上面2)一样
            }
            //可能pi与pi+1俩x星,需要变1--N个si出来,如下图,本身si和pi+2后面就相等的
            //这得看si相同的字符出现了几个?前缀
            while(si < str.length && (pattern[pi] == '.' || pattern[pi] == str[si])){
                //只要是pi为点,或者pi与si相同,一定能配上,先让x星变1个si出来
                if (f2(str, pattern, si + 1, pi + 2, dp)) {
                    dp[si][pi] = 1;
                    return true;
                }
                si++;//si跟着动,pi不动,继续,就是看看让x星变2 3 N个si出来,与pi+2之后匹配
            }//整体就是x星变0 1 2  N个si出来,有其一就OK

            //4)上面所有的这些过程都走完,我们已经考虑周全了,都没法成功,那不好意思,失败了
            dp[si][pi] = 0;
            return false;
        }

看看这次LeetCode
LeetCode高频题10:正则表达式匹配_第28张图片
不管如何,还是比最开始提高了一点点
本题就这样了没法再骚了


总结

提示:重要经验:

1)本题中,‘.’ 匹配任意单个字符,点可以单独出现,‘*’ 匹配零个或多个前面的那一个元素,星必须配合前面一个字符使用,不能单独出现
2)最重要的是f尝试从s的si–N-1上尽量让pattern从pi–M-1上匹配出来,主要看pi和pi+1处的点与星的状况,区分好,区分清楚才能写出来,然后改记忆化搜索代码
3)笔试求AC,可以不考虑空间复杂度,但是面试既要考虑时间复杂度最优,也要考虑空间复杂度最优。

你可能感兴趣的:(大厂面试高频题之数据结构与算法,leetcode,正则表达式,正则匹配,字符串s和p,数据结构与算法)