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

'.' 匹配任意单个字符
'*' 匹配零个或多个前面的那一个元素

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

说明:

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

示例 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 = "c*a*b"
输出: true
解释: 因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。

示例 5:

输入:
s = "mississippi"
p = "mis*is*p*."
输出: false

这道求正则表达式匹配的题和那道 Wildcard Matching 的题很类似,不同点在于*的意义不同,在之前那道题中,*表示可以代替任意个数的字符,而这道题中的*表示之前那个字符可以有0个,1个或是多个,就是说,字符串a*b,可以表示b或是aaab,即a的个数任意,这道题的难度要相对之前那一道大一些,分的情况的要复杂一些,需要用递归Recursion来解,大概思路如下:

- 若p为空,若s也为空,返回true,反之返回false。

- 若p的长度为1,若s长度也为1,且相同或是p为'.'则返回true,反之返回false。

- 若p的第二个字符不为*,若此时s为空返回false,否则判断首字符是否匹配,且从各自的第二个字符开始调用递归函数匹配。

- 若p的第二个字符为*,进行下列循环,条件是若s不为空且首字符匹配(包括p[0]为点),调用递归函数匹配s和去掉前两个字符的p(这样做的原因是假设此时的星号的作用是让前面的字符出现0次,验证是否匹配),若匹配返回true,否则s去掉首字母(因为此时首字母匹配了,我们可以去掉s的首字母,而p由于星号的作用,可以有任意个首字母,所以不需要去掉),继续进行循环。

- 返回调用递归函数匹配s和去掉前两个字符的p的结果(这么做的原因是处理星号无法匹配的内容,比如s="ab", p="a*b",直接进入while循环后,我们发现"ab"和"b"不匹配,所以s变成"b",那么此时跳出循环后,就到最后的return来比较"b"和"b"了,返回true。再举个例子,比如s="", p="a*",由于s为空,不会进入任何的if和while,只能到最后的return来比较了,返回true,正确)。

解法一:

class Solution {
public:
bool isMatch(string s, string p) {
if (p.empty()) return s.empty();
if (p.size() == 1) {
return (s.size() == 1 && (s[0] == p[0] || p[0] == '.'));
}
if (p[1] != '*') {
if (s.empty()) return false;
return (s[0] == p[0] || p[0] == '.') && isMatch(s.substr(1), p.substr(1));
}
while (!s.empty() && (s[0] == p[0] || p[0] == '.')) {
if (isMatch(s, p.substr(2))) return true;
s = s.substr(1);
}
return isMatch(s, p.substr(2));
}
};

上面的方法可以写的更加简洁一些,但是整个思路还是一样的,我们先来判断p是否为空,若为空则根据s的为空的情况返回结果。当p的第二个字符为*号时,由于*号前面的字符的个数可以任意,可以为0,那么我们先用递归来调用为0的情况,就是直接把这两个字符去掉再比较,或者当s不为空,且第一个字符和p的第一个字符相同时,我们再对去掉首字符的s和p调用递归,注意p不能去掉首字符,因为*号前面的字符可以有无限个;如果第二个字符不为*号,那么我们就老老实实的比较第一个字符,然后对后面的字符串调用递归,参见代码如下:

解法二:

class Solution {
public:
bool isMatch(string s, string p) {
if (p.empty()) return s.empty();
if (p.size() > 1 && p[1] == '*') {
return isMatch(s, p.substr(2)) || (!s.empty() && (s[0] == p[0] || p[0] == '.') && isMatch(s.substr(1), p));
} else {
return !s.empty() && (s[0] == p[0] || p[0] == '.') && isMatch(s.substr(1), p.substr(1));
}
}
};

我们也可以用DP来解,定义一个二维的DP数组,其中dp[i][j]表示s[0,i)和p[0,j)是否match,然后有下面三种情况(下面部分摘自这个帖子):

1.  P[i][j] = P[i - 1][j - 1], if p[j - 1] != '*' && (s[i - 1] == p[j - 1] || p[j - 1] == '.');
2.  P[i][j] = P[i][j - 2], if p[j - 1] == '*' and the pattern repeats for 0 times;
3.  P[i][j] = P[i - 1][j] && (s[i - 1] == p[j - 2] || p[j - 2] == '.'), if p[j - 1] == '*' and the pattern repeats for at least 1 times.

解法三:

class Solution {
public:
bool isMatch(string s, string p) {
int m = s.size(), n = p.size();
vector<vector<bool>> dp(m + 1, vector<bool>(n + 1, false));
dp[0][0] = true;
for (int i = 0; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
if (j > 1 && p[j - 1] == '*') {
dp[i][j] = dp[i][j - 2] || (i > 0 && (s[i - 1] == p[j - 2] || p[j - 2] == '.') && dp[i - 1][j]);
} else {
dp[i][j] = i > 0 && dp[i - 1][j - 1] && (s[i - 1] == p[j - 1] || p[j - 1] == '.');
}
}
}
return dp[m][n];
}
};

正则表达式匹配 C++实现 java实现 leetcode系列(十一)的更多相关文章

  1. java基础解析系列(十一)---equals、==和hashcode方法

    java基础解析系列(十一)---equals.==和hashcode方法 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系 ...

  2. BUGFIX 09 - 记一次Java中String的split正则表达式匹配 - 引发`OutOfMemoryError: Java heap space`的oom异常 排查及解决 -Java根据指定分隔符分割字符串,忽略在引号里面的分隔符

    问题简述 说白了,Java根据指定分隔符分割字符串,忽略在引号(单引号和双引号)里面的分隔符; oom压测的时候,正则匹配"(?=(?:[^\"]*\"[^\" ...

  3. 字符串转换整数 (atoi) C++实现 java实现 leetcode系列(八)

    字符串转换整数 (atoi) java实现 C++实现 请你来实现一个 atoi 函数,使其能将字符串转换成整数. 首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止. 当 ...

  4. Z 字形变换 C++实现 java实现 leetcode系列(六)

    Z 字形变换  java实现 C++实现  将一个给定字符串根据给定的行数,以从上往下.从左到右进行 Z 字形排列. 比如输入字符串为 "LEETCODEISHIRING" 行数为 ...

  5. 最长回文子串 C++实现 java实现 leetcode系列(五)

    给定一个字符串 s,找到 s 中最长的回文子串.你可以假设 s 的最大长度为 1000. 示例 1: 输入: "babad" 输出: "bab" 注意: &qu ...

  6. 整数转罗马数字 C++实现 java实现 leetcode系列(十二)

    罗马数字包含以下七种字符: I, V, X, L,C,D 和 M. 字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000 例如, 罗马数字 2 写做 II ,即为两个并 ...

  7. 整数翻转C++实现 java实现 leetcode系列(七)

    给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转. 示例 1: 输入: 123 输出: 321 示例 2: 输入: -123 输出: -321 示例 3: 输入: 120 输出: ...

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

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

  9. Java实现 LeetCode 10 正则表达式匹配

    10. 正则表达式匹配 给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配. '.' 匹配任意单个字符 '*' 匹配零个或多个前面的那一个元素 所谓匹配, ...

随机推荐

  1. 0x07 贪心

    被虐爆了...贪心这种玄学东西还可以证吗??除了范围缩放算是可以想想比较经典(倍增第一题?)... poj3614:这道题想了很久,并没有想到是把minSPF按大到小排序,一直的思想是小的就小到大排序 ...

  2. Fragment间相互调用并传值

    public class MainFragment extends Fragment { private static final String ARG_DATE="com.example. ...

  3. TimSort学习资料

    深入理解 timsort 算法(1):自适应归并排序 如何找出Timsort算法和玉兔月球车中的Bug? Java TimSort算法 源码 笔记 Timsort https://en.wikiped ...

  4. QlikSense系列(1)——整体介绍

    接触QlikSense(3.1 SR1)已经快一年了,在此记录自己的经验心得,为想了解QlikSense的小伙伴提供一个参考. 1.产品介绍 Qlik公司以QlikView产品成名,QlikSense ...

  5. Git+VS2015修改提交代码以及解决冲突

    第一步:前提我们已经讲代码从git上clone(可以看我的GIt篇)下来,创建自己的开发分支.我们在自己本地的分支上开发. 看到右下角有一个develop那是我自己建立的开发的分支,现在点击devel ...

  6. c/s winform打包和部署

    1:vs2010新建  安装项目  左边出现3个文件夹 2:点击 第一个文件夹-> “应用程序文件夹”    将 bin 目录下的所以文件  拖进 右边的空白处:c:\windows\syste ...

  7. 移动端 | Vue.js对比微信小程序基础语法

    (1)vue 自定义组件与父组件的通信,props:[abb],可以看成自组建的一个自定义属性 (2)vue 模版语法{{}} 只能是在DOM中插入,<div>{{acc}}</di ...

  8. css实战笔记(一):写网页前的reset工作

    reset.css是每个html必备的样式,其中有各种元素属性清零的代码. 为什么要有reset.css 让各个浏览器的CSS样式有一个统一的基准,比如清除各个浏览器为元素自带的margin.padd ...

  9. FloatActionButton弹出菜单

    浮动按钮的弹出菜单动画 将几个按钮重叠摆放,使用ValueAnimator更新按钮的坐标实现. 布局 <FrameLayout android:layout_width="match_ ...

  10. mac 下安装mcrypt 扩展

    参考: http://coolestguidesontheplanet.com/how-to-install-mcrypt-for-php-on-mac-osx-lion-10-7-developme ...