动态规划: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代码:

class Solution(object):
    def isMatch(self, s, p):
        """
        :type s: str
        :type p: str
        :rtype: bool
        """
        sL = len(s)
        pL = len(p)
        f = [[False for j in range(pL + 1)] for i in range(sL + 1)]
        f[0][0] = True
        for i in range(0, sL + 1):
            for j in range(1, pL + 1):
                if p[j - 1] != '*':
                    if match(s,i,p,j):
                        f[i][j] = f[i -1][j - 1]
                else:
                    if match(s,i, p ,j -1):
                        f[i][j] = f[i - 1][j] or f[i][j - 2]
                    else:
                        f[i][j] = f[i][j - 2]
        return f[sL][pL]
        
def match(s, i, p, j):
    if s[i - 1] == p[j - 1]:
        return True
    if p[j - 1] == '.':
        return True
    return False



        

总结:

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

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

版权声明:本文为博主作者:若石之上原创文章,版权归属原作者,如果侵权,请联系我们删除!

原文链接:https://blog.csdn.net/duzm200542901104/article/details/136459664

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
乘风的头像乘风管理团队
上一篇 2024年4月22日
下一篇 2024年4月22日

相关推荐