https://leetcode.com/problems/regular-expression-matching/
经典题目,考虑DP的多种情况。
个人解答
class Solution:
def isMatch(self, s: str, p: str) -> bool:
@functools.lru_cache(None)
def match(si, pi):
if si == -1:
# consider optional pattern
while pi > 0 and p[pi] == '*':
pi -= 2
return pi == -1
if si < 0 or pi < 0:
return False
if s[si] == p[pi] or p[pi] == '.':
return match(si - 1, pi - 1)
if p[pi] == '*':
if p[pi - 1] == s[si] or p[pi - 1] == '.':
return match(si - 1, pi) or match(si, pi - 1) or match(si, pi - 2)
else:
return match(si, pi - 2)
return False
return match(len(s) - 1, len(p) - 1)
题目分析
https://leetcode.com/problems/regular-expression-matching/discuss/5651/Easy-DP-Java-Solution-with-detailed-Explanation
(上述解答里边有错误,关键的DP公式:
2 if p.charAt(i-1) == s.charAt(i) or p.charAt(i-1) == '.':
应该是:
2 if p.charAt(j-1) == s.charAt(i) or p.charAt(j-1) == '.':
这道题一开始不知道如何入手,想着parser那一套,似乎也不太行,后来看到DP的解法,豁然开朗。
其实就是,踏踏实实找状态,还是比较容易想到的。
分情况讨论:
- 最后一个字符相同(包括
.
的情况):dp[si][pi] = dp[si - 1][pi - 1]
- 最后一个字符是
*
:- 通配符前边的字符相同(包括
.
的情况),选择很多,可以要该字符0/1/2…次:dp[si][pi] = dp[si][pi - 2] or dp[si][pi - 1] or dp[si - 1][pi]
- 通配符前边的字符相同(包括
- 非法情况,return false
其中需要注意两点:
- 要通配符的字符2+次,关系是:
dp[si][pi] = dp[si - 1][pi]
,还是要保留通配符,但是s前移了一位,这样递推下去就能做到通配2+次的效果 - 考虑pattern冗余的情况,比如到最后,s已经空了,但是p中还有
a*b*
这样的结构,此时需要消除掉