提示:本题是系列LeetCode的150道高频题,你未来遇到的互联网大厂的笔试和面试考题,基本都是从这上面改编而来的题目
互联网大厂们在公司养了一大批ACM竞赛的大佬们,吃完饭就是设计考题,然后去考应聘人员,你要做的就是学基础树结构与算法,然后打通任督二脉,以应对波云诡谲的大厂笔试面试题!
你要是不扎实学习数据结构与算法,好好动手手撕代码,锻炼解题能力,你可能会在笔试面试过程中,连题目都看不懂!比如华为,字节啥的,足够让你读不懂题
这就是网易的招聘面试题,你得有本事才能拿下,现场手撕代码,写出来才能进去赚钱,拿高薪!
如果拿不下这类的题目,不好意思,网易不会招聘你的,就是这么现实!
给你一个字符串 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可能有小写,可能有点,可能有星
比如:
d*可以变为三个d
星配合前面的字符使用,变任意0–N个d来
一个点,可以变一个字符
点和星配合使用非常强大,可以变任意个点出来,然后由点去变任意字符,非常强大!!!
匹配失败的情况:
点一定要配出来一个字符哦!!!
不过出现强大的点星配合就是可以,让点星变0个点完事了
本题是真的挺复杂的
之前咱们老说的四种互联网大厂必考的动态规划模型:
【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不能出问题
(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的开头决不能是星,这是基本原则!!!
(1)基础情况就是当si都走到了str的N位置,越界了,则s的N–N-1就是一个空串""
这个时候,pattern的pi–M-1咋着才能匹配出空串呢?
要么pi也是到了pattern的末尾M位置,M–M-1也是空串""
要么pi出有字符,但是从j=pi+1开始,j往后每次递增2,知道M-1全部都是星,这样保证x星y星z星等等这种模型出现
才能让x星变0个x,就是空串"",这样才能匹配
如果上面两种情况都不行
pi是x,但是pi+1位置没法出星,那不好意思,完蛋
手撕代码:
//(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的空串,是没有办法变出字符来的……
//(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不会是星,开头位置不会是星的哦
如果pi+1处是点,你还得保证str的si+1之后与pi+1之后能配上【不能只看si和pi呗】
比如:
总之,你就是要保证,在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往后一串匹配上才行
3)再往下pi和si一定能配上了,因为pi可能是点,可能与si相同,而且pi+1就是星
可能pi与pi+1俩x星,需要变0个si出来,如下图,本身si和pi+2后面就相等的
可能需要可能pi与pi+1俩x星,变1个si出来,如下图,本身si+1和pi+2后面就相等的
可能需要可能pi与pi+1俩x星,变2个si出来,如下图,本身si+2和pi+2后面就相等的
可能需要可能pi与pi+1俩x星,变3个si出来,如下图,本身si+3和pi+2后面就相等的
上面不断去变,有一个成功,就算能匹配上
这个过程,用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后面能否匹配上?
……
整体就是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
但是呢,速度有点慢,当然了………………
暴力递归f肯定速度慢
面试的目的,是需要你优化的,2变量si和pi,样本位置对应模型
是咱们的DP3,完全可以在这暴力递归的基础上改动态规划
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],就像下面那个表
f其实针对每一个si和pi的组合,都会有一个结果的
咱们要的是谁?
f(0,0),即放入dp[0][0],下图五角星那个位置
最开始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反响:
这个已经是动态规划了,可能这种题目,基本没法节约太多的时间
甭管了
有可能是因为其中某些骚操作常数时间还挺复杂度
再改一下常数时间那些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
不管如何,还是比最开始提高了一点点
本题就这样了没法再骚了
提示:重要经验:
1)本题中,‘.’ 匹配任意单个字符,点可以单独出现,‘*’ 匹配零个或多个前面的那一个元素,星必须配合前面一个字符使用,不能单独出现
2)最重要的是f尝试从s的si–N-1上尽量让pattern从pi–M-1上匹配出来,主要看pi和pi+1处的点与星的状况,区分好,区分清楚才能写出来,然后改记忆化搜索代码
3)笔试求AC,可以不考虑空间复杂度,但是面试既要考虑时间复杂度最优,也要考虑空间复杂度最优。