Skip to content

Latest commit

 

History

History
114 lines (83 loc) · 3.87 KB

10.-zheng-ze-biao-da-shi-pi-pei.md

File metadata and controls

114 lines (83 loc) · 3.87 KB

10. 正则表达式匹配

https://leetcode-cn.com/problems/regular-expression-matching/

解法一:递归

这里要注意的是“*”表示前面的字符可以重复0到无数次,并不是传统意义上的通配符。

X*(X为任意字符),当做一个整体,这是最难处理的部分,因为x可以重复0到无数次:

  1. 出现0次:s不变,p跳过X*部分,右移两位:isMatch(s, p[2:])
  2. 出现1~n次:s与p匹配一次(first_match),且s右移一位,p不变(保持X*):first_match and isMatch(s[1:], p)
class Solution:
    def isMatch(self, s: str, p: str) -> bool:
        if not p:
            return not s
        #为了使代码简洁,第一位字符匹配的结果用first_match代替
        #首先s不能为空,然后看第0位是否相等,或者p第0位是否为'.'
        first_match = bool(s) and (s[0] == p[0] or p[0] == '.')
        
        #处理'X*'字段
        if len(p) >= 2 and p[1] == '*':
            return self.isMatch(s, p[2:]) or \      #'X*'出现0次
            (first_match and self.isMatch(s[1:], p))    #'X*'出现n次
        #常规匹配
        else:
            return first_match and self.isMatch(s[1:], p[1:])

解法二:DP

用二维矩阵dp记录,dp[i][j]的布尔值表示s[0..i)是否与p[0..j)匹配(注意右边是开区间),初始值为False。
初始状态:令dp[0][0]为true,i=0表示s为空,j=0表示p为空,s、p都为空的时候是匹配的。
重点还是处理X*类型的p串:

情况1):判断“a”和“ab*”是否匹配

s=“a” , i=1(注意前面表述是开区间)
   ^
p=“a b *” , j=3
       ^

b*匹配0次,则j回退两位,i不动:

s=“a” , i=1
   ^
p=“a b *” , j=1
   ^

若“a”和“a”匹配,则“a”和“ab*”匹配。
即dp[i][j]的状态和dp[i][j-2]是相同的,推导得dp[i][j] = dp[i][j-2]

情况2):判断“abb...”和“ab*”是否匹配

s=“a b b...” , i=2
     ^
p=“a b *” , j=3
       ^

b*匹配1~n次,则i回退1位,j回退2位

s=“a b b...” , i=1
   ^
p=“a b *” , j=1
   ^    

若“a”和“a”匹配,且“a”和“ab*_”_匹配,则“ab”和“ab*”匹配。

“a”和“a”匹配的条件为:s[i-1] == p[j-2] ,或p[j-2] == ‘.’(单个字符匹配)

“a”和“ab*”匹配的条件:有点抽象,因为b可以匹配0到无数个b,因此要保证上一个状态是匹配的(模式串p不动;s串回退一位,类似递归中的情况2),当前状态才能匹配。上一个状态为:dp[i-1][j] = true 综上,a匹配一次的条件:

dp[i-1][j] and (s[i-1] == p[j-2] or p[j-2] == '.')

情况1)和2)是并列的。

情况3):单个字符匹配:
两种,一种是相等匹配,另一种是‘.’匹配。而且上一个状态dp[i-1][j-1]匹配
条件:

dp[i-1][j-1] and (s[i-1] == p[j-1] or p[j-1] == '.')

此外还要注意下标,要保证i>0

因为有p[j-1] == ‘*’的限制,因此j不用过多限制,且根据题意,’*‘之前一定有东西,len(p)>=2

从0到m ,j从1到n,因为j=0时p为空串,除了空以外的所有s都不匹配

class Solution:
    def isMatch(self, s: str, p: str) -> bool:
        m, n = len(s), len(p)
        dp = [[False] * (n+1) for _ in range(m+1)]
        dp[0][0] = True
        for i in range(m+1):
            for j in range(1, n+1):
                if p[j-1] == '*':
                    dp[i][j] = dp[i][j-2] or (i>0 and dp[i-1][j] and ((s[i-1] == p[j-2]) or p[j-2] == '.'))
                else:
                    dp[i][j] = i>0 and dp[i-1][j-1] and (s[i-1] == p[j-1] or p[j-1] == '.')
                    
        return dp[m][n]