四时宝库

程序员的知识宝库

突破LeetCode,拿BAT大厂offer之《正则表达式匹配》(动态规划)

导读:算法哥前面分享了一个《通配符匹配》,有粉丝留言,算法哥你再讲讲leetcode上另一道《正则表达式匹配》,正则表达式匹配这道题是前面通配符匹配的加强版,大家一起来学习吧!

题目描述

给定一个字符串 (s) 和一个字符模式 (p)。实现支持 '.' 和 '*' 的正则表达式匹配。

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

匹配应该覆盖整个字符串 (s) ,而不是部分字符串。

题目来源:LeetCode 10:Regular Expression Matching

说明:

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

示例 1:

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

示例 2:

输入:
s = "aa"
p = "a*"
输出: true
解释: '*' 代表可匹配零个或多个前面的元素, 即可以匹配 'a' 。因此, 重复 'a' 一次, 字符串可变为 "aa"。

示例 3:

输入:
s = "ab"
p = ".*"
输出: true
解释: ".*" 表示可匹配零个或多个('*')任意字符('.')。

示例 4:

输入:
s = "aab"
p = "c*a*b"
输出: true
解释: 'c' 可以不被重复, 'a' 可以被重复一次。因此可以匹配字符串 "aab"。

示例 5:

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

题目分析

这道题目已经有正则表达式的意思在里面了,.表示任意字符,*表示零个或多个前面的元素。这道题目和突破LeetCode,拿BAT大厂offer之《通配符匹配》(动态规划)的区别在于,前者的*只是表示任意字符串,后者表示零个或者多个前面的元素。


我们还是和前面一道题同样的思路,令dp[i][j]表示字符串s前i个字符和模式串p的前j个字符的匹配情况,匹配等于1,不匹配等于0

那么我们怎么根据dp数组记录的状态进行状态转移呢?也就是说dp[i][j]可以由哪些状态递推过来呢?我们一起来看图吧!

情形1:

显然绿色位置已经匹配,所以dp[1][1]=1,我们考察红色的位置,也就是dp[2][2],此时因为s[2]=p[2],且dp[1][1]=1,所以显然dp[2][2]=1;

情形2:

绿色部分表示已经匹配了,此时dp[10][9]=1,此时p[10]=.,s[11]=i,因为.匹配任意字符,所以dp[11][10] = 1;

情形3:

大家注意到图片里新增加了下标0,目的是说明dp[0][0]=1,且dp[0][2]等于什么呢?,哈哈此时p[2]=*,重复0次,是不是dp[0][2]=1!注意这个情形哦,不然样例4就解释不过了!

情形4:

此时dp[1][3]=1,那dp[1][4]等于什么呢?因为p[4]=*,重复一次,dp[1][[4]=1.


总结一下:

  1. 情形1和情形2对应了由dp[i-1][j-1]推导dp[i][j];
  2. 情形3对应了由dp[i][j-2]推导dp[i][j];
  3. 情形4对应了由dp[i][j-1]推导dp[i][j];

聪明的读者肯定注意到了那dp[i-1][j]推导dp[i][j]呢?这个就留给读者自己思考吧,读者也可以对照算法哥给出的源码自己画图推导!

上源码:


复杂度分析

两层for循环,时间复杂度是O(mn)的,m表示字符串s的长度,n表示模式串p的长度;空间复杂度是O(mn)的,这里依然可以用滚动数组优化到O(n),不会的同学就翻算法哥以前的文章吧!


题目总结

这个题目比前面的《通配符匹配》难度上要大一点,状态转移如果不能抓住问题的本质,还是有一定难度的,通过这个题目,相信大家对算法哥说的大问题转化为小问题,小问题推导大问题又有了新的认识吧!


题目分享完毕,请帮助算法哥上头条吧,您的关注,转发,点赞,评论,都是对算法哥最大的鼓励!

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接