Leetcode演算法——44、正則式匹配
給定一個輸入字串 s 和一個模式字串 p,實現正則匹配,支援’?‘和’*’。
規則:
- ‘?’ 可以匹配任何單個字元
- ‘*’ 可以匹配任何字元的序列(包括空序列)
- 需要匹配整個輸入字串 s,不能部分匹配。
備註:
- 字串 s 只會包含小寫 a-z,可以為空。
- 字串 p 只會包含小寫 a-z、? 或 *,可以為空。
Example 1:
Input:
s = “aa”
p = “a”
Output: false
Explanation: “a” does not match the entire string “aa”.
Example 2:
Input:
s = “aa”
p = ""
Output: true
Explanation: '
Example 3:
Input:
s = “cb”
p = “?a”
Output: false
Explanation: ‘?’ matches ‘c’, but the second letter is ‘a’, which does not match ‘b’.
Example 4:
Input:
s = “adceb”
p = “ab”
Output: true
Explanation: The first ‘’ matches the empty sequence, while the second '’ matches the substring “dce”.
Example 5:
Input:
s = “acdcb”
p = “a*c?b”
Output: false
思路
這道題與Leetcode演算法——10、實現正則匹配很相似,不同之處在於匹配規則不同。
1、遞迴法
從左到右依次掃描 s 和 p 的每個字元,看是否可以匹配。
- 如果 s 為空,則只有 p 全部為 *,才能匹配上。
- 否則:
-
- 如果首字元相同,或者 p 首字元為 ?,則可以匹配上,繼續遞迴匹配 s[1:] 和 p[1:]
-
- 如果 p 首字元為 * ,則可以嘗試兩種情況:
-
- (1)試探讓 * 表示 s 的首字元,s 右移一位,* 保留,繼續遞迴匹配 s[1:] 和 p
-
- (2)試探讓 * 表示空字元,略過 *,s不變,繼續遞迴匹配 s 和 p[1:]
-
- 這兩種情況是 or 的關係,只要有一個為真,則說明可以匹配上。
2、改進版
沿用上面的遞迴方法的思路,額外開闢一個空間:將上述過程中的每次子字串的匹配結果,專門用一個詞典儲存起來,這樣之後再次用到的話,直接取出即可,不必再計算一遍。
為什麼有可能再次用到?關鍵在於上面說的特殊情況,即 p 的首字元是 * 的情況。這時候會分成兩種情況,兩者是 or 的關係,都需要計算出結果,這時候就可能會重複計算了。
python實現
def isMatch(s, p):
"""
:type s: str
:type p: str
:rtype: bool
遞迴法。
"""
if not p:
return not s
if s: # s不為空
if p[0] in [s[0], '?']: # 首字元相同,或者p首字元為?,都可以匹配上
return isMatch(s[1:], p[1:]) # 各右移一位,繼續匹配
elif p[0] == '*': # p首字元為*
# isMatch(s[1:], p)試探讓*表示s的首字元,s右移一位,*保留
# isMatch(s, p[1:])試探讓*表示空字元,略過*,s不變
return isMatch(s[1:], p) or isMatch(s, p[1:])
elif p[0] == '*': # s為空,則只有p全部為*,才可以匹配上
return isMatch(s, p[1:])
return False
def isMatch2(s, p):
"""
:type s: str
:type p: str
:rtype: bool
改進版。
"""
p.replace('**','*')
memo = {}
def dp(i, j):
if (i, j) not in memo:
ans = False
if j == len(p):
ans = i == len(s)
elif i < len(s):
if p[j] in [s[i], '?']:
ans = dp(i+1, j+1)
elif p[j] == '*':
if (i, j+1) in memo:
ans = dp(i, j+1) or dp(i+1, j)
else:
ans = dp(i+1, j) or dp(i, j+1)
elif p[j] == '*':
ans = dp(i, j+1)
memo[i, j] = ans
return memo[i, j]
return dp(0, 0)
if '__main__' == __name__:
s = "adceb"
p = "*a*b"
print(isMatch(s, p))