Leetcode10. 正则表达式匹配 (Python3测试通过) - 动态规划 (手把手推导动态转移方程)

一、题目描述:

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

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

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

  1. 示例 1:

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

  2. 示例 2:

    输入:s = “aa”, p = “a*”
    输出:true
    解释:因为 ‘*’ 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 ‘a’。因此,字符串 “aa” 可被视为 ‘a’ 重复了一次。

  3. 示例 3:

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

  • 提示:
    • 1 <= s.length <= 20
    • 1 <= p.length <= 30
    • s 只包含从 a-z 的小写字母。
    • p 只包含从 a-z 的小写字母,以及字符 . 和 *。
    • 保证每次出现字符 * 时,前面都匹配到有效的字符

二、解决思路和代码

思路:动态规划
状态转移方程讲解

举例:字符串 s = a a a b c a a b s = aaabcaab s=aaabcaab 和一个字符规律 p = a ∗ b . ∗ b p = a*b.*b p=ab.b,实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。

现在我们给出一个表格 d p dp dp 记录每个状态下,字符串的匹配情况。表格中第 i i i 行,第 j j j 列中元素 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示的含义是: s 的前 i 个字符 (s[:i]) 与 p 的前 j 个字符 (p[:j]) 是否匹配。若匹配 dp[i][j]=‘T’, 不匹配 dp[i][j]=‘F’。

1. 初始化dp
  • dp[:][0] = [T, F, F, F, F, F, F, F, F]

  • dp[0][1:]

  • 由上, dp 初始表格为:

2. 逐行求dp
  • i = 1 i = 1 i=1 , s[:1] = ‘a’

    • dp表格更新过程如下:

    • 所以,dp[1][1:] = [T, T, F, F, F, F]。更新 dp 表格:

  • i = 2 i = 2 i=2 , s[:2] = ‘aa’

    • dp表格更新过程如下:

    • 所以,dp[2][1:] = [F, T, F, F, F, F]。更新 dp 表格:

  • i = 3 i = 3 i=3 , s[:3] = ‘aaa’

    • dp表格更新过程如下:

    • 所以,dp[3][1:] = [F, T, F, F, F, F]。更新 dp 表格:

      在这里插入图片描述

  • i = 4 i = 4 i=4 , 自己动手画一下吧。。。

3. 总结归纳
  • 根据逐行求解 dp 过程,得:

    • p [ j ] ≠ s [ i ]  and  p [ j ] ≠  "."  p[j]\ne s[i] \text{ and } p[j] \ne \text{ "." } p[j]=s[i] and p[j]= "."  时,
      d p [ i ] [ j ] = F a l s e dp[i][j] = False dp[i][j]=False
    • p [ j ] = s [ i ]  or  p [ j ] =  ’.’  p[j]=s[i] \text{ or } p[j] = \text{ '.' } p[j]=s[i] or p[j]= ’.’  时,
      d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] dp[i][j] = dp[i-1][j-1] dp[i][j]=dp[i1][j1]
    • p [ j ] = ’*’ p[j] = \text{'*'} p[j]=’*’ 时,
      • (1) 对于 d p [ i ] [ j ] dp[i][j] dp[i][j],观察上述 s [ : 3 ] s[:3] s[:3] p [ : 2 ] p[:2] p[:2] 的匹配过程,可以发现:
        d p [ i ] [ j ] = d p [ i − 0 ] [ j − 2 ]   a n d   ( p [ j − 1 ] ≠ s [ i − 0 ]   a n d   p [ j − 1 ] ≠ ’.’ )   o r   d p [ i − 0 ] [ j − 2 ]   a n d   ( p [ j − 1 ] = s [ i − 0 ]   o r   p [ j − 2 ] = ’.’ )   o r   d p [ i − 1 ] [ j − 2 ]   a n d   ( p [ j − 2 ] = s [ i − 1 ]   o r   p [ j − 2 ] = ’.’ )   o r   d p [ i − 2 ] [ j − 2 ]   a n d   ( p [ j − 2 ] = s [ i − 2 ]   o r   p [ j − 2 ] = ’.’ )   o r    ......    d p [ i − k ] [ j − 2 ]   a n d   ( p [ j − 2 ] = s [ i − k ]   o r   p [ j − 2 ] = ’.’ ) \begin{alignat}{2} dp[i][j] & = dp[i-0][j-2]\ and\ \left ( p[j-1]\ne s[i-0]\ and\ p[j-1]\ne \text{'.'} \right ) \ \mathbf{or} \\ & \quad \ {\color{Red} dp[i-0][j-2]\ and\ \left ( p[j-1]=s[i-0]\ or\ p[j-2]=\text{'.'} \right ) \ \mathbf{or}} \\ & \quad \ {\color{Green} dp[i-1][j-2]\ and\ \left ( p[j-2]=s[i-1]\ or\ p[j-2]=\text{'.'} \right ) \ \mathbf{or}} \\ & \quad \ {\color{Green} dp[i-2][j-2]\ and\ \left ( p[j-2]=s[i-2]\ or\ p[j-2]=\text{'.'} \right ) \ \mathbf{or}} \\ & \quad \ {\color{Green} \text{ ...... }} \\ & \quad \ {\color{Green} dp[i-k][j-2]\ and\ \left ( p[j-2]=s[i-k]\ or\ p[j-2]=\text{'.'} \right ) } \end{alignat} dp[i][j]=dp[i0][j2] and (p[j1]=s[i0] and p[j1]=’.’) or dp[i0][j2] and (p[j1]=s[i0] or p[j2]=’.’) or dp[i1][j2] and (p[j2]=s[i1] or p[j2]=’.’) or dp[i2][j2] and (p[j2]=s[i2] or p[j2]=’.’) or  ......  dp[ik][j2] and (p[j2]=s[ik] or p[j2]=’.’)

      • (2) 对于 d p [ i − 1 ] [ j ] dp[i-1][j] dp[i1][j]:
        d p [ i − 1 ] [ j ] = d p [ i − 1 ] [ j − 2 ]   a n d   ( p [ j − 2 ] ≠ s [ i − 1 ]   a n d   p [ j − 2 ] ≠ ’.’ )   o r   d p [ i − 1 ] [ j − 2 ]   a n d   ( p [ j − 2 ] = s [ i − 1 ]   o r   p [ j − 2 ] = ’.’ )   o r   d p [ i − 2 ] [ j − 2 ]   a n d   ( p [ j − 2 ] = s [ i − 2 ]   o r   p [ j − 2 ] = ’.’ )   o r    ......    d p [ i − ( k − 1 ) ] [ j − 2 ]   a n d   ( p [ j − 2 ] = s [ i − ( k − 1 ) ]   o r   p [ j − 2 ] = ’.’ ) \begin{alignat}{2} dp[i-1][j] & = dp[i-1][j-2]\ and\ \left ( p[j-2]\ne s[i-1]\ and\ p[j-2]\ne \text{'.'} \right ) \ \mathbf{or}\\ & \quad \ {\color{Green} dp[i-1][j-2]\ and\ \left ( p[j-2]=s[i-1]\ or\ p[j-2]=\text{'.'} \right ) \ \mathbf{or}} \\ & \quad \ {\color{Green} dp[i-2][j-2]\ and\ \left ( p[j-2]=s[i-2]\ or\ p[j-2]=\text{'.'} \right ) \ \mathbf{or}} \\ & \quad \ {\color{Green} \text{ ...... }} \\ & \quad \ {\color{Green} dp[i-\left ( k-1 \right ) ][j-2]\ and\ \left ( p[j-2]=s[i-\left ( k-1 \right ) ]\ or\ p[j-2]=\text{'.'} \right ) } \end{alignat} dp[i1][j]=dp[i1][j2] and (p[j2]=s[i1] and p[j2]=’.’) or dp[i1][j2] and (p[j2]=s[i1] or p[j2]=’.’) or dp[i2][j2] and (p[j2]=s[i2] or p[j2]=’.’) or  ......  dp[i(k1)][j2] and (p[j2]=s[i(k1)] or p[j2]=’.’)

      • (3) 由上述 (1) 和 (2) 推到得:
        d p [ i ] [ j ] = d p [ i − 0 ] [ j − 2 ]   a n d   ( p [ j − 1 ] ≠ s [ i ]   o r   p [ j − 1 ] ≠ ’.’ )   o r   d p [ i − 0 ] [ j − 2 ]   a n d   ( p [ j − 1 ] = s [ i ]   o r   p [ j − 1 ] = ’.’ )   o r   d p [ i − 1 ] [ j ]   a n d   ( p [ j − 1 ] = s [ i ]   o r   p [ j − 1 ] = ’.’ ) = d p [ i − 0 ] [ j − 2 ]   o r   d p [ i − 1 ] [ j ]   a n d   ( p [ j − 1 ] = s [ i ]   o r   p [ j − 1 ] = ’.’ ) \begin{alignat}{2} dp[i][j] & = dp[i-0][j-2]\ and\ \left ( p[j-1]\ne s[i]\ or\ p[j-1]\ne \text{'.'} \right ) \ \mathbf{or} \\ & \quad \ {\color{Red} dp[i-0][j-2]\ and\ \left ( p[j-1]=s[i]\ or\ p[j-1]=\text{'.'} \right ) \ \mathbf{or}} \\ & \quad \ {\color{Green} dp[i-1][j]\ and\ \left ( p[j-1]=s[i]\ or\ p[j-1]=\text{'.'} \right )}\\ & = dp[i-0][j-2]\ \mathbf{or} \\ & \quad \ {\color{Green} dp[i-1][j]\ and\ \left ( p[j-1]=s[i]\ or\ p[j-1]=\text{'.'} \right )} \end{alignat} dp[i][j]=dp[i0][j2] and (p[j1]=s[i] or p[j1]=’.’) or dp[i0][j2] and (p[j1]=s[i] or p[j1]=’.’) or dp[i1][j] and (p[j1]=s[i] or p[j1]=’.’)=dp[i0][j2] or dp[i1][j] and (p[j1]=s[i] or p[j1]=’.’)
        这里,可以理解为: d p [ i ] [ j ] = d p [ i ] [ j − 2 ] dp[i][j] = dp[i][j-2] dp[i][j]=dp[i][j2]零个 * 前面的字符的情况; d p [ i ] [ j ] = d p [ i − 1 ] [ j ] dp[i][j] = dp[i-1][j] dp[i][j]=dp[i1][j]1个 或者 多个 * 前面的字符的情况。

  • 正则表达式匹配的状态转移方程为:
    d p [ i ] [ j ] = { F a l s e , p [ j ] ≠ s [ i ]  and  p [ j ] ≠ ’.’ d p [ i − 1 ] [ j − 1 ] , p [ j ] = s [ i ]  or  p [ j ] = ’.’ d p [ i ] [ j − 2 ]   o r   d p [ i − 1 ] [ j ]   a n d   ( p [ j − 1 ] = s [ i ]   o r   p [ j − 1 ] = ′ . ′ ) , p [ j ] = ’*’ dp[i][j] = \begin{cases} False, & p[j]\ne s[i] \text{ and } p[j]\ne \text{'.'}\\ dp[i-1][j-1], & p[j]=s[i] \text{ or } p[j]=\text{'.'}\\ dp[i][j-2]\ \mathbf{or} \ dp[i-1][j]\ and \ (p[j-1]=s[i]\ or\ p[j-1]='.'), & p[j]=\text{'*'}\\ \end{cases} dp[i][j]= False,dp[i1][j1],dp[i][j2] or dp[i1][j] and (p[j1]=s[i] or p[j1]=.),p[j]=s[i] and p[j]=’.’p[j]=s[i] or p[j]=’.’p[j]=’*’

代码

class Solution:
    def isMatch(self, s: str, p: str) -> bool:
        
        ## 初始化
        dp = [[False for i in range(len(p)+1)] for j in range(len(s)+1)]
        dp[0][0] = True
        for idx in range(1, len(dp[0])):
            if p[idx-1] == '*' and idx-2>=0:
                dp[0][idx] = dp[0][idx-2]
                
        ## 根据状态转移方程更新dp
        for i in range(1, len(dp)):
            for j in range(1, len(dp[0])):
                if p[j-1]==s[i-1] or p[j-1]=='.':
                    dp[i][j] = dp[i-1][j-1]
                if p[j-1]=='*':
                    dp[i][j] = dp[i-1][j] if p[j-2]==s[i-1] or p[j-2]=='.' else dp[i][j-2]
        return dp[-1][-1]
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值