10.正则表达式匹配
思路:字符串 s, 字符规律p
- 匹配s第一个字符是否是 ‘.’ 或者相等
- 判断下一个是否是’ * ‘,是则优先匹配’ * '后一个,否则p无法前进,无法匹配再回来匹配当前第一个, 成功匹配则p前进到下一个
- 第一个匹配成功,且第二个不是星号,则p和s同时前进
- 递归每次匹配一到两个字符
代码实现
class Solution:
def isMatch(self, s: str, p: str) -> bool:
# 当p走到最后,s走完为True ,没走完为False
if not p: return not s
# 判断当前第一个字符
same = True if s and p[0] in {'.', s[0]} else False
# 有星号匹配第三位,不匹配p无法前进
# 匹配第一位
# 无星号,匹配同步前进
return len(p) >= 2 and p[1] == '*' and (self. isMatch(s,p[2:]) or same and self.isMatch(s[1:],p)) or same and self.isMatch(s[1:],p[1:])
动态规划
动态规划有一个比较混淆的点就是下标/索引
以 s= “aab” p="cab"为例
初始图
1. 用一个二维数组来表示动态表,由图可以看出
- dp[ i ][ j ]表示的是s[i-1] 与p[j-1]的匹配情况
- 第一个中括号代表第 i 行 对应s的下标 i-1,第二个中括号代表 j 列对应p的下标 j-1
- 按图中当匹配成功就改 False 为 True,当遍历结束,将True顺利传递到最后,即匹配成功
- 往后的匹配依赖前面的匹配结果
2. 初始化dp表预留第一行第一列来保存
- 初始化星号✳️所在列第0行为标记为True
- dp[0][0] 预设为True相当于在匹配之前虚构一个成功匹配
3. 条件判断
a. 没有遍历到星号✳️:
比较当前 s[i-1] == p[j-1] or p[j-1] == ‘.’ 考虑到匹配成功p和s 各进一位,把左上的True传递给当前位置dp[i][j] = dp[i - 1][j - 1]
b. 遍历到星号✳️ p[j-1] == ‘*’ 分两种情况:
- 星号是0到多次,比较前缀找到匹配 :s[i-1] == p[j - 2] or p[j- 2] == ‘.’ :
匹配时,匹配时 dp[i][j] = dp[i][j - 2] or dp[i - 1][j] , 分两种情况:
(1) 第一个星号匹配成功接受第0行的星号 dp[i][j] = dp[ i-1 ][ j ]
(2) 后续星号传递前缀之前的匹配值即 dp[ i ][ j ] = dp[ j ][ j-2 ] - 不匹配,跳过前缀+星号 dp[i][j] = dp[i][j - 2]
1. 匹配过程匹配成功的True的传递(p=“cab, s=“aab”)
2.匹配过程匹配成功的True的传递(p=“acb, s=“aab”)
class Solution:
def isMatch( s: str, p: str) -> bool:
ls, lp = len(s), len(p)
# 初始化动态表
dp = [[False for _ in range(lp + 1)] for _ in range(ls + 1)]
dp[0][0] = True
# 动态检查的前置条件
for j in range(2, lp + 1):
dp[0][j] = dp[0][j - 2] and p[j - 1] == '*'
# 遍历动态表
for i in range(1, ls + 1):
for j in range(1, lp + 1):
if p[j-1] == '*':
# 星号前的符号与当前字符匹配
if s[i-1] == p[j - 2] or p[j- 2] == '.':
dp[i][j] = dp[i][j - 2] or dp[i - 1][j]
else:
dp[i][j] = dp[i][j - 2]
elif s[i-1] == p[j-1] or p[j-1] == '.':
dp[i][j] = dp[i - 1][j - 1]
return dp[-1][-1]