第一版:p的2各字符一组匹配,分析.,x状况
"""
10. 正则表达式匹配
给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。
'.' 匹配任意单个字符
'*' 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。
示例 1:
输入:s = "aa" p = "a"
输出:false
解释:"a" 无法匹配 "aa" 整个字符串。
示例 2:
输入:s = "aa" p = "a*"
输出:true
解释:因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。
示例 3:
输入:s = "ab" p = ".*"
输出:true
解释:".*" 表示可匹配零个或多个('*')任意字符('.')。
示例 4:
输入:s = "aab" p = "c*a*b"
输出:true
解释:因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。
示例 5:
输入:s = "mississippi" p = "mis*is*p*."
输出:false
"""
#踩了无数坑的正则表达式:1)合并同类a* a*; 否则一旦最终是匹配错误的会不停循环出无数组合(a*爆发而非收敛)
# # 2)剩余长度:s=p=0成功; s=0,p!=0 看是否剩余了x*; 不剩余x*,或者s!=,p=0 (sp=0)全错
# 3)正式匹配: 字母+'*' (字母不能配上,字母能配上),.+'*', '.'开头,普通开头
class Solution:
def isMatch(self, s: str, p: str) -> bool:
if len(p) == 0 and len(s) == 0:
return True # 截止条件之一,同时匹配空掉了
if len(p) == 1:
# return True if s == p else False
if p != '.':
return True if s == p else False
else:
return True if len(s) == 1 else False
if len(p) >= 2 and p[1] == '*' and len(s) == 0:
return self.isMatch(s, p[2:])
if len(p) * len(s) == 0:
return False # 两个只有1个为0,错误
# 多个同类的 a*a*a*相当于1个
if len(p) >= 4 and p[0] == p[2] and p[1] == '*' and p[3] == '*':
return self.isMatch(s, p[2:])
if s[0:2] == p[0:2]: # 2个字符为单位进行递归,尽量每次去掉一个字符
return self.isMatch(s[1:], p[1:])
elif p[0] == '.' and p[1] == '*': # 最强组合.*,匹配任意字符串(只要没有猪队友)
return self.isMatch(s, p[2:]) or self.isMatch(s[1:], p[2:]) or self.isMatch(s[1:], p)
# 匹配0个,1个,1+个
elif p[0] == '.':
print(s[0:], s[0] + p[1:])
return self.isMatch(s[0:], s[0] + p[1:]) # 开头.,替换成s对应的字符
elif s[0] != p[0] and p[1] == '*':
return self.isMatch(s, p[2:])
# elif s[0] == p[0] and p[1] == '.' and len(s) > 1:
# return self.isMatch(s[1:], s[1] + p[2:]) or self.isMatch(s[1:], p[1:]) #保留.用作.*结构的强匹配
elif s[0] == p[0] and p[1] == '*':
return self.isMatch(s, p[2:]) or self.isMatch(s[1:], p[2:]) or self.isMatch(s[1:], p)
# 配0个,*匹配1个(原始a*对应a),匹配1+个(对应aa*)(+1个是可以反复递归的,一直递归到s中再没有匹配的p[0]为止)
# self.isMatch(s[1:], p)
elif s[0] == p[0]: # 得过且过,过完这个,下一个也许会出现x*结构 #and s[1] != p[1]
return self.isMatch(s[1:], p[1:])
else:
return False
第二版:只看p的第二个符号做分支选择
稍微改进:合并同类的逻辑判断:
class Solution:
def isMatch(self, s: str, p: str) -> bool:
if len(p)==0 and len(s) == 0:
return True #截止条件之一,同时匹配空掉了
if len(p) == 1:
#return True if s == p else False
if p != '.':
return True if s == p else False
else:
return True if len(s) == 1 else False
if len(s) == 0 and p[1] == '*':
return self.isMatch(s, p[2:])
if len(s) * len(p) == 0:
print(1)
return False
#合并同类 x*,防止匹配膨胀,太过贪婪
if len(p) > 3 and p[1] == '*' and p[0:2] == p[2:4]:
return self.isMatch(s, p[2:])
if p[1] == '*' and p[0] == '.':
return self.isMatch(s, p[2:]) or self.isMatch(s[1:], p) # self.isMatch(s[1:], p[2:]) or /
elif p[1] == '*' and p[0] == s[0]:
return self.isMatch(s, p[2:]) or self.isMatch(s[1:], p)
elif p[1] == '*' and p[0] != s[0]:
return self.isMatch(s, p[2:])
elif p[0] == '.':
return self.isMatch(s[1:], p[1:])
elif p[0] == s[0]:
return self.isMatch(s[1:], p[1:])
else:
# print(2, s, p)
return False
代码简化了,逻辑上复杂在预先判断剩余长度0、长度1的状况
第三版:大神版,利用len(s) p[0]in大为简化判断
- 递归的很溜溜溜
- 注意合并同类(a* a* a* a*)等,否者重复递归浪费大量循环试错
class Solution:
def isMatch(self, s: str, p: str) -> bool:
if len(p) == 0:
#print(s, not s)
return not s
match = (len(s) > 0) and (p[0] in [s[0], '.'])
if len(p) > 3 and p[1] == '*' and p[0:2] == p[2:4]: #合并同类的a*加速明显(避免多轮重复迭代)15504ms减少到88ms
return self.isMatch(s, p[2:])
if len(p) > 1 and p[1] == '*':
#print(match, s, p, ' match ', s and (p[0] in [s[0], '.']))
return self.isMatch(s, p[2:]) or (match and self.isMatch(s[1:], p)) #match and 配0并不一样要配上;
#print(match)
return match and self.isMatch(s[1:], p[1:])