首先左神的代码是从递归暴力开始慢慢优化到动态规划的。而且是从左到右做匹配。
以下代码是直推的方式。
思路:从右往左匹配更合理,也就是当两个串的最后的字符匹配,剩余字符也匹配(动态规划已知),就匹配。
三种大情况:
(为什么是i-1而不是i?可能0到i-1就是i个字符?)
当s[i-1] 和 p[j-1]匹配时,这个串是否匹配看s[0:i-1]和p[0:j-1]是否匹配就行。匹配dp[i][j]存true,不匹配dp[i][j]存false
当s[i-1] 和 p[j-1]不匹配时,不意味着s和p不匹配。因为当p[j-1]是“”时还是有可能匹配的。
当p[j-1]不是,可能不匹配。
当p[j-1]是,要分情况讨论:
第一种是当p[j-2] 匹配了s[i-1] ,要考虑三种情况,因为p[j-2] 可以消失,存在一次,存在多次,其中有一种匹配都是匹配的。
1、 p[j-2]消失的情况下,也就是考察s[0:j-1]和p[0:j-2],即dp[j-1][j-2]
2、p[j-2]存在一次,也就是考察s[0:j-2]和p[0:j-2],即dp[j-2][j-2]
3、p[j-2]存在多次,其实也是考察s[0:j-2]和p[0:j-2],即dp[j-2][j-2]?
第二种是当p[j-2] 没匹配了s[i-1] ,但是这样还是有可能匹配。此时看p[j-3]和s[i-1]是否匹配。
第三种是当
class Solution:
def isMatch(self, s: str, p: str) -> bool:
# dp[0][0]代表s为空串,p为空串,是否匹配。
dp = [[False for j in range(len(p) + 1)] for i in range(0, len(s) + 1)]
# base case:
dp[0][0] = True
for j in range(1, len(dp[0])):
if p[j-1] != "*":
dp[0][j] = False
else:
if j >= 2:
dp[0][j] = dp[0][j-2]
# print(dp)
for i in range(1, len(dp)):
for j in range(1, len(dp[i])):
if s[i-1] == p[j-1] or p[j-1] == ".": # 当最后一个字符和pattern的最后一个字符匹配了
dp[i][j] = dp[i-1][j-1]
elif p[j-1] != "*": # and s[i] != p[j] and p[j] != "." 当最后一个字符和pattern的最后一个字符没匹配而且pattern的最后一个字符不是*
dp[i][j] = False
else: # and s[i] != p[j] and p[j] == "*" 当最后一个字符和pattern的最后一个字符没匹配,但是pattern的最后一个字符是*
# 看s的最后一次字符和pattern倒数第二个字符是否匹配,
# 如果不匹配,那就取决于p的倒数第三个数以前的匹不匹配s全部的了。
if s[i-1] != p[j-2] and p[j-2] != ".":
if j >= 2:
dp[i][j] = dp[i][j-2]
# 如果匹配,那就看取多少个了。可能取0个,1个,和多个,取0个意味着p倒数第二个匹配了但也删除,那就取决于p的倒数第三个了
# 1个,和多个的情况一样,都是取决于s的倒数第二个数以前的和p整个有没有匹配。
else:
dp[i][j] = dp[i-1][j] or dp[i][j-2]
return dp[-1][-1]