正则表达式匹配(难度:困难)
1
转自:
作者:labuladong
链接:https://leetcode-cn.com/problems/regular-expression-matching/solution/ji-yu-guan-fang-ti-jie-gen-xiang-xi-de-jiang-jie-b/
来源:力扣(LeetCode)
一、处理点号「.」通配符
点号可以匹配任意一个字符,其实是最简单的:
1 2 3 4 def isMatch (text, pattern ) -> bool : if not pattern: return not text first_match = bool (text) and pattern[0 ] in {text[0 ], '.' } return first_match and isMatch(text[1 :], pattern[1 :])
二、处理「*」通配符
星号通配符可以让前一个字符重复任意次数,包括零次。那到底是重复几次呢?这需要计算机暴力穷举来算,假设重复
N 次吧。写递归的技巧是管好当下,之后的事抛给递归。具体到这里,不管 N
是多少,当前的选择只有两个:匹配 0 次、匹配 1 次。所以可以这样处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 def isMatch (text, pattern ) -> bool : if not pattern: return not text first_match = bool (text) and pattern[0 ] in {text[0 ], '.' } if len (pattern) >= 2 and pattern[1 ] == '*' : return isMatch(text, pattern[2 :]) or \ first_match and isMatch(text[1 :], pattern) else : return first_match and isMatch(text[1 :], pattern[1 :])
可以看到,我们是通过保留 pattern 中的「*」,同时向后推移
text,来实现「*」将字符重复匹配多次的功能。
三、动态规划
暴力解法
1 2 3 4 5 6 7 8 9 10 11 class Solution : def isMatch (self, s: str , p: str ) -> bool : if not p: return not s first = s and p[0 ] in {s[0 ],'.' } if len (p) >= 2 and p[1 ]=='*' : return self .isMatch(s,p[2 :]) or (first and self .isMatch(s[1 :],p)) else : return first and self .isMatch(s[1 :],p[1 :])
2
优化解法
使用两个变量 i
, j
记录当前匹配到的位置,从而避免使用子字符串切片,并且将 i
,
j
存入memo,避免重复计算。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution : def isMatch (self, s: str , p: str ) -> bool : memo = {} def dp (i,j ): if (i,j) in memo: return memo[(i,j)] if j==len (p): return i==len (s) first = i < len (s) and p[j] in {s[i],'.' } if len (p) >= j+2 and p[j+1 ]=='*' : return dp(i,j+2 ) or (first and dp(i+1 ,j)) else : return first and dp(i+1 ,j+1 ) memo[(i,j)] = ans return ans return dp(0 ,0 )
3
复杂度
时间复杂度:用 T 和 P 分别表示匹配串和模式串的长度。对于 i=0, ... , T
和 j=0, ... , P 每一个 dp(i, j) 只会被计算一次,所以后面每次调用都是
O(1)的时间。因此,总时间复杂度为 O(TP) 。
空间复杂度:我们用到的空间仅有 O(TP)个 boolean
类型的缓存变量。所以,空间复杂度为 O(TP) 。