• 动态规划:LeetCode第10题 正则表达式匹配


    题目:

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

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

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

    示例 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 <= 20
    • s 只包含从 a-z 的小写字母。
    • p 只包含从 a-z 的小写字母,以及字符 . 和 *
    • 保证每次出现字符 * 时,前面都匹配到有效的字符

    思考方向:

    1、字符串是否匹配取决于字符串每一位的匹配结果,如果前一位字符都不匹配,则后续将不会再匹配了,所以适合动态规划的结题思路,也即:后面的结果可以由前面的结果推导出来

    2、因为字符串匹配是两个字符串,所以采用二维的动态规划

    3、*匹配零个或多个前面的那一个元素,这个是结题的关键,尤其是匹配零个,这个经常让我们忽略,比如下例:

    输入:s = "a", p = "ab*"

    输出:True 

    解释:"b" 后面有*,表示b出现了0次。

    动态规划的状态转移方程:

    我们用 f[i][j]表示 s的前 i个字符与 p中的前 j 个字符是否能够匹配。

    在进行状态转移时,我们考虑 p 的第 j 个字符的匹配情况:

    1、如果 p 的第 j 个字符是一个小写字母( '.' 匹配任意一个字母,可以看成一个特殊的字母),那么我们必须在 s 中匹配一个相同的小写字母,若相等,只需判断 i,j 之前的字符串是否匹配即可,也就转化为子问题 f[i-1][j-1],若不等,则当前的 i,j 肯定不能匹配,则f[i][j]= false.即可写出公式:

    2、如果 p 的第 j 个字符是 *,那么就表示我们可以对 p 的第 j−1 个字符匹配0次或多次,则不妨把类似 'a*', 'b*' 等的当成整体看待:

    2.1、如果p[j-1]与s[i]不匹配,那么f[i][j]的匹配情况需要看 f[i][j-2],也就是a*匹配0次(等于没出现a*)的情况,那么也就转化为子问题 f[i][j-2]

    2.2、如果p[j-1]与s[i]匹配

    2.2.1、最先想到的是一种情况:因为s[i]已经与p[j-1]匹配了,所以我们要看s[i]与p[j−2]的匹配,也就是转化为子问题:f[i][j-2]

    2.2.2、其次还有一种情况:2.2.1考虑的是p[j]是p[j-1]重复的情况,本场景需要考虑的是s[i]与s[i-1]相同的情况,那就看s[i−1]与p[j] 的匹配,也就是转化为子问题:f[i-1][j]

    2.2.3、综上,那么即可写出公式:

    3、最终的状态转换方程如下:

    4、状态初始化:

    4.1、动态规划的边界条件为 f[0][0]=true,f[n][0](n>0)=false,也就是空字符串s与空字符串p相等,而任何的非空字符串s与空字符串p与都不相等。有人会问,为什么空字符串s能与非空字符串p相等呢?因为 a*能匹配空字符串,此时表示0次匹配。

    Python代码:

    1. class Solution(object):
    2. def isMatch(self, s, p):
    3. """
    4. :type s: str
    5. :type p: str
    6. :rtype: bool
    7. """
    8. sL = len(s)
    9. pL = len(p)
    10. f = [[False for j in range(pL + 1)] for i in range(sL + 1)]
    11. f[0][0] = True
    12. for i in range(0, sL + 1):
    13. for j in range(1, pL + 1):
    14. if p[j - 1] != '*':
    15. if match(s,i,p,j):
    16. f[i][j] = f[i -1][j - 1]
    17. else:
    18. if match(s,i, p ,j -1):
    19. f[i][j] = f[i - 1][j] or f[i][j - 2]
    20. else:
    21. f[i][j] = f[i][j - 2]
    22. return f[sL][pL]
    23. def match(s, i, p, j):
    24. if s[i - 1] == p[j - 1]:
    25. return True
    26. if p[j - 1] == '.':
    27. return True
    28. return False

    总结:

    1、动态规划是逆向思考问题,从后往前进行归纳,也就是思考f(n+1)是如何由f(n)得到的问题

    2、动态规划方程的初始化也需要思考清楚,相关的边界条件要想好

  • 相关阅读:
    光致发光荧光量子产率测试系统
    【云原生系列】第四讲:Knative 之 Eventing
    数据库复习带答案
    电容搞搞”振“,PDN有帮衬
    百度百家号旋转验证码识别研究
    【C++】C++多线程库的使用
    C语言从入门到高级
    【TensorFlow2 之012】TF2.0 中的 TF 迁移学习
    Redis —— 一次错误记录
    基于flink与groovy实现全实时动态规则智能营销与风控系统
  • 原文地址:https://blog.csdn.net/duzm200542901104/article/details/136459664