一.题目链接:https://leetcode.com/problems/regular-expression-matching/

二.题目大意:

  实现一个正则表达式,该正则表达式只有两种特殊的字符——“.”和“*”,其中.能表示任意字符,即它可以匹配任意的字符;*表示可以重复前面的字符0次或者多次(例如:a*可以表示成“”(空,相当于重复0次)或者表示成“aaa”重复3次)。

三.题解:

  这道题目比较常用的方法是递归;该题目的难点之处在于遇到*的时候它可能匹配0次,也可能匹配1次或多次;这种特性很显然满足递归的特性。所以要根据字符是否为*来进行分情况讨论,详细如下:

对于一个模式字符串,从前往后的来分析它:

1.如果当前字符的下一个字符不是“*”的话(由于第一个字符不可能是* 所以根据下一个字符是否为*来分情况才更加的合理),此时只需要判断需匹配字符串s的当前字符和模式字符串p的当前字符是否相等即可。如果*s == *p或者*p == '.' && *s != '\0'的话,那么当前字符匹配成功,就可以继续往下匹配了。

2.如果当前字符的下一个字符是“*”的话,此时就需要匹配当前字符0次、1次或多次了(而此处的重复次数可以用递归法来实现,也可以用迭代法来实现),直到不能匹配更多的字符。

3.既然是递归,那么一定·要有终止条件的,该问题的终止条件显然是:当p到达终点时(即*p == '\0'时)如果s也到达终点了,那么返回true;如果s没到达终点,那么返回false。通过这个条件我们也可以看出,字符串p的最终长度一定是与字符串s的长度一样,才能够匹配成功。(这也就是题目时所说的完全匹配,而不是部分匹配。通过这一点,我们可也快速的去除那些不符合匹配的例子,如:ab和.*c,两者显然不匹配;而ab和.* 两者显然匹配)

具体代码如下:

方法1(递归+部分迭代):

class Solution {
public:
bool isMatch(string s, string p) {
return matchCore(s.c_str(),p.c_str());
}
bool matchCore(const char *s,const char *p)
{
if(*p == '\0' && *s == '\0')
return true;
if(*p == '\0' && *s != '\0')
return false;
//如果下一个字符不是*的话
if(*(p+1) != '*')
{
if(*p == *s||(*p == '.' && *s != '\0'))
return matchCore(s + 1,p + 1);
else//一旦有一个字符不一样,则不匹配
return false;
}
else//下一个字符是*
{
//*重复一次或多次,只有当当前字符匹配成功时,才考虑*重复多少次,否则不用考虑*了,直接跳过
while(*p == *s ||(*p == '.' && *s != '\0'))
{
if(matchCore(s,p + 2))
return true;
s++;
}
//*不再重复,即直接跳过*
return matchCore(s,p + 2);
} }
};

注:

1.该方法中while循环的部分相当于对*进行匹配1次或多次,而matchCore(s,p+2)相当于忽略* ,即匹配0次。下面来详细阐述一下此处过程:

(1)首先,先判断p和s的当前字符是否相同,如果不相同的话,* 这个字符就可以忽略了,相当于匹配0次,即执行matchCore(s,p+2);

(2)如果p和s的当前字符相同的话,此时要考虑对*进行处理了,即匹配0次、1次还是多次?首先先尝试匹配0次,如果匹配0次的话,p和s的剩下部分都相匹配的话,那么直接就可以返回true了,if(machCore(s,p+2) retrun true;这两行代码就是这个意思。如果匹配0次的话不可行怎么办?此时就是s++相当于匹配1次(如果if(matchCore(s,p+2)这个条件成立的话,相当于匹配1次就成功了),匹配一次完之后再进行判断p和s的当前字符是否相同,然后重复之前的过程;这样就完成了匹配0次、1次或多次这整个过程。

2.一定要注意递归函数的终止条件!!

3.此处有个c++方法需要注意,即string.c_str()的用法,它的功能是将一个string类型的字符串转换成一char *字符串。

方法2(全部递归):

class Solution {
public:
bool isMatch(string s, string p) {
return matchCore(s.c_str(),p.c_str());
}
bool matchCore(const char *s,const char *p)
{
if(*p == '\0' && *s == '\0')
return true;
if(*p == '\0' && *s != '\0')
return false;
//如果下一个字符不是*的话
if(*(p+1) != '*')
{
if(*p == *s||(*p == '.' && *s != '\0'))
return matchCore(s + 1,p + 1);
else//一旦有一个字符不一样,则不匹配
return false;
}
else//下一个字符是*
{ if(*p == *s || (*p == '.' && *s != '\0'))//只有当当前字符匹配成功时,才考虑*重复多少次,否则不用考虑*了,直接跳过
return matchCore(s,p + 2) || matchCore(s + 1,p + 2) || matchCore(s + 1,p); //*重复0次、一次或多次
else
return matchCore(s, p + 2);
} }
};

注:

全部递归的话,比之前的"递归+迭代"法满了不少,方法1用了23ms,但方法2(本方法)用了731ms。所以能不用递归就不用递归,这句话是很有道理的;但递归法写的代码确实看上去更加容易理解,虽然相比迭代有时会爆栈或超时(超时很有可能是添加了多余的操作,有时这种多余的操作并不那么的明显)

方法3(全部递归-优化):

class Solution {
public:
bool isMatch(string s, string p) {
return matchCore(s.c_str(),p.c_str());
}
bool matchCore(const char *s,const char *p)
{
if(*p == '\0' && *s == '\0')
return true;
if(*p == '\0' && *s != '\0')
return false;
//如果下一个字符不是*的话
if(*(p+1) != '*')
{
if(*p == *s||(*p == '.' && *s != '\0'))
return matchCore(s + 1,p + 1);
else//一旦有一个字符不一样,则不匹配
return false;
}
else//下一个字符是*
{ if(*p == *s || (*p == '.' && *s != '\0'))//只有当当前字符匹配成功时,才考虑*重复多少次,否则不用考虑*了,直接跳过
return matchCore(s,p + 2) || matchCore(s + 1,p); //*重复匹配s中字符0次、一次或多次
else
return matchCore(s, p + 2);//如果当前字符不相同的话,把该字符和*一块去掉(相当于重复0次),去匹配pattern+2后的部分
} }
};

方法3相比方法2,主要是优化了一处,即

 return matchCore(s,p + 2) || matchCore(s + 1,p + 2) || matchCore(s + 1,p);

变成了

return matchCore(s,p + 2) || matchCore(s + 1,p);

因为仔细想想,重复多次是由多个重复1次组成的啊,所以最终不用再强调重复一次了,直接用matchCore(s+1,p)既可以表示重复1次也可以表示成重复多次。

那么去除这一步后,耗时多少呢?是26ms!!可见递归并不比迭代慢,主要是是否不增加多余的操作。话说,做到这一步心里很是挺爽的~~

此外,还有需要注意的一点:对于任何字符串处理的题目,首先都需要判断该字符串是否为nullptr(空指针)或者为""的情况,这种特例一定是要考虑的!!(对于char *类型的字符串,这两种情况都需要考虑;对于string类型的字符串,一般只考虑""的情况)

LeetCode——10. Regular Expression Matching的更多相关文章

  1. leetcode 10 Regular Expression Matching(简单正则表达式匹配)

    最近代码写的少了,而leetcode一直想做一个python,c/c++解题报告的专题,c/c++一直是我非常喜欢的,c语言编程练习的重要性体现在linux内核编程以及一些大公司算法上机的要求,pyt ...

  2. Leetcode 10. Regular Expression Matching(递归,dp)

    10. Regular Expression Matching Hard Given an input string (s) and a pattern (p), implement regular ...

  3. leetcode 10. Regular Expression Matching 、44. Wildcard Matching

    10. Regular Expression Matching https://www.cnblogs.com/grandyang/p/4461713.html class Solution { pu ...

  4. [LeetCode] 10. Regular Expression Matching 正则表达式匹配

    Given an input string (s) and a pattern (p), implement regular expression matching with support for  ...

  5. LeetCode (10): Regular Expression Matching [HARD]

    https://leetcode.com/problems/regular-expression-matching/ [描述] Implement regular expression matchin ...

  6. [LeetCode] 10. Regular Expression Matching

    Implement regular expression matching with support for '.' and '*'. DP: public class Solution { publ ...

  7. Java [leetcode 10] Regular Expression Matching

    问题描述: Implement regular expression matching with support for '.' and '*'. '.' Matches any single cha ...

  8. [leetcode]10. Regular Expression Matching正则表达式的匹配

    Given an input string (s) and a pattern (p), implement regular expression matching with support for  ...

  9. 蜗牛慢慢爬 LeetCode 10. Regular Expression Matching [Difficulty: Hard]

    题目 Implement regular expression matching with support for '.' and '*'. '.' Matches any single charac ...

  10. [LeetCode] 10. Regular Expression Matching ☆☆☆☆☆

    Implement regular expression matching with support for '.' and '*'. '.' Matches any single character ...

随机推荐

  1. JavaBasic_07

    面向对象三大特征 1.封装 封装是一种信息隐藏技术 a.是指数据和基于数据的操作封装在一起,数据被保护在内部(类的内部(对象)) b.系统的其他部分只有通过在数据外面的被授权的操作才能进行交互(没有授 ...

  2. CFG文件格式

    大多数情况下,很多程序都要保存用户的设置,办法有很多:注册表,日志文件·..... 而很多程序都使用了一个专用的文件.为了方便起见,常常命名为*.cfg,有时甚至直接命名为Config.cfg. 这只 ...

  3. Unity 灯光系统详解

    Unity 灯光系统详解 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- 心分享.心 ...

  4. lesson9-小象学院cv

    www.sohu.com/a/159976204_717210 生成模型:基于联合概率~共生关系判别模型:基于条件概率~因果关系 生成模型之学习数据分布:1)概率密度函数估计 2)数据样本生成 模型目 ...

  5. java sftp 报错 Permission denied (没有权限;拒绝访问)

    解决办法: 1.检查账号密码是否错误 2.检查freeSSHD是否是以管理员身份运行的 3.检查sftp路劲有没有配置错误,java通过sftp将图片文件传输到指定文件夹,如果这个文件夹在配置的当前目 ...

  6. 阿里druid数据库连接池缓存方案

    阿里缓存机制:若在进某一页面的时候执行了select语句,会将该select语句查询出来的数据存入缓存,若执行了修改语句则清空该缓存,若没有执行修改语句则再次进入此页面的时候会直接从缓存中加载上次se ...

  7. linux 的IP配置和网络问题的排查

    1.6  IP的配制, 首先要会用: ifconfig  和加相关参数如: ifconfig -a, 来查看,自己的电脑网络配制. 再次就必需要知道,默认IP配制文件的地方: cd /etc/sysc ...

  8. controller层,service层,dao层(main函数,子函数,子的子函数)

    controller层相当于main函数————————————————————————————————————————————————————@RequestMapping("/query ...

  9. mariadb增量备份

    何为增量备份,简单理解就是使用日志记录每天数据库的操作情况,只需要每天把这个日志里的数据库操作还原到数据库中,从而避免每天都进行完全备份,这种情况下,每周进行一次完全备份即可 首先我们需要配置以下ma ...

  10. 我发起并创立了一个 VMBC 的 子项目 D#

    大家好, 我发起并创立了一个 VMBC 的 子项目 D#  . 有关 VMBC ,  请参考 <我发起了一个 用 C 语言 作为 中间语言 的 编译器 项目 VMBC>     https ...