Leetcode 10:正則表示式匹配(最詳細的解法!!!)
阿新 • • 發佈:2018-11-16
給定一個字串 (s
) 和一個字元模式 (p
)。實現支援 '.'
和 '*'
的正則表示式匹配。
'.' 匹配任意單個字元。
'*' 匹配零個或多個前面的元素。
匹配應該覆蓋整個字串 (s
) ,而不是部分字串。
說明:
s
可能為空,且只包含從a-z
的小寫字母。p
可能為空,且只包含從a-z
的小寫字母,以及字元.
和*
。
示例 1:
輸入:
s = "aa"
p = "a"
輸出: false
解釋: "a" 無法匹配 "aa" 整個字串。
示例 2:
輸入: s = "aa" p = "a*" 輸出: true 解釋: '*' 代表可匹配零個或多個前面的元素, 即可以匹配 'a' 。因此, 重複 'a' 一次, 字串可變為 "aa"。
示例 3:
輸入:
s = "ab"
p = ".*"
輸出: true
解釋: ".*" 表示可匹配零個或多個('*')任意字元('.')。
示例 4:
輸入:
s = "aab"
p = "c*a*b"
輸出: true
解釋: 'c' 可以不被重複, 'a' 可以被重複一次。因此可以匹配字串 "aab"。
示例 5:
輸入:
s = "mississippi"
p = "mis*is*p*."
輸出: false
解題思路
這個問題如果以暴力破解方式思考其實挺難的,主要的難點在於*
匹配多少次的問題,所以我們可以先嚐試通過遞迴解決這個問題。
對於*
len(p)>1 and p[1]=="*"
,如果是的話,說明*
在p
的第二位,我們就要判斷s[0]
和p[0]
能否匹配,如果可以匹配的話,我們繼續判斷isMatch(s[1:], p)
(也就是*
匹配了一次,我們會繼續使用.*
或者?*
去參與比較)。如果上述條件都不成立,我們可以判斷isMatch(s,p[2:])
是不是成立(也就是*
表示匹配0
次)。
如果*
不在p
的第二位,我們就要判斷s[0]
和p[0]
能否匹配。
class Solution:
def isMatch(self, s, p):
"""
:type s: str
:type p: str
:rtype: bool
"""
s_len, p_len = len(s), len(p)
if p_len == 0:
return s_len == 0
if p_len > 1 and p[1] == "*":
return self.isMatch(s, p[2:]) or \
(s_len != 0 and (s[0] == p[0] or p[0] == '.') \
and self.isMatch(s[1:], p))
else:
return s_len != 0 and (s[0] == p[0] or p[0] == '.')\
and self.isMatch(s[1:], p[1:])
對於遞迴可以解決的問題,我們都可以記憶花搜尋的方式來優化。
class Solution:
def isMatch(self, s, p):
"""
:type s: str
:type p: str
:rtype: bool
"""
s_len, p_len = len(s), len(p)
mem = [[None]*(p_len+1) for _ in range(s_len+1)]
return self._isMatch(s, p, mem)
def _isMatch(self, s, p, mem):
s_len, p_len = len(s), len(p)
if p_len == 0:
return s_len == 0
if mem[s_len][p_len] != None:
return mem[s_len][p_len]
if p_len > 1 and p[1] == "*":
mem[s_len][p_len] = self._isMatch(s, p[2:], mem) or \
(s_len != 0 and (s[0] == p[0] or p[0] == '.') \
and self._isMatch(s[1:], p, mem))
return mem[s_len][p_len]
else:
mem[s_len][p_len] = s_len != 0 and (s[0] == p[0] or p[0] == '.')\
and self._isMatch(s[1:], p[1:], mem)
return mem[s_len][p_len]
然而記憶化搜尋又和動態規劃有著密切的聯絡,所以我們可以非常迅速的寫出動態規劃轉移方程
f(i,j)
表示輸入s[0:i]
和輸入p[0:j]
時的匹配結果。
class Solution:
def isMatch(self, s, p):
"""
:type s: str
:type p: str
:rtype: bool
"""
s_len, p_len = len(s), len(p)
mem = [[False]*(p_len+1) for _ in range(s_len+1)]
mem[0][0] = True
for i in range(s_len+1):
for j in range(1, p_len+1):
if p[j-1] == '*':
mem[i][j] = mem[i][j-2] or \
(i > 0 and (s[i-1] == p[j-2] or \
p[j-2] == ".") and mem[i-1][j])
else:
mem[i][j] = i > 0 and \
mem[i-1][j-1] and \
(s[i-1] == p[j-1] or p[j-1] == ".")
return mem[s_len][p_len]
我在之前的一些問題中也一直提及這樣的問題,如果我們拿到一個動態規劃問題無從下手的時候,不防先從遞迴開始思考。
我將該問題的其他語言版本新增到了我的GitHub Leetcode
如有問題,希望大家指出!!!