一、题设
给你一个字符串 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 解释:".*" 表示可匹配零个或多个('*')任意字符('.')。
二、基本思路
这题属于动态规划题。
首先,由题意知,‘*’可以匹配多个字符,‘.’可匹配单个字符。那么若s="abc"时,p="ab*”或p="ab."都可以适配s;若s="abc"时,p="a*"也可以适配它,但是p="a."就无法适配s。我们记动态数组flag[a][b] = false表示s[0:a-1]与p[0:b-1]不适配。
所以p与s的匹配分为以下几种情况:
1. 当p[j-1] != '*'时,那么就比较p[j-1]与s[i-1]是否相等,或p[j-1]="."也可以(p[j-1]可以当成任意字符与s[i-1]匹配),若相等,则flag[i][j] = flag[i-1][j-1](比较前s[0,i-2]与p[0,j-2]即可)
2.当p[j-1] == '*'时,那么可以将p[j-2]与'*'看成一个整体,即表示p[j-2]在s中匹配了0次或多次。值得注意的是匹配了0次和多次只要有一次成功即为匹配成功。
2.1 若p[j-2]与'*'匹配了0次,那么即p[j-2]与‘*’自己消除了,所以flag[i][j] = flag[i][j-2](比较前s[0,i-1]与p[0,j-3]即可)。
2.2 若p[j-2]与'*'匹配了多次(0次以上),判断p中"*"前的字符与s最后一个字符是否相同或者p中"*"前的字符为'.'也一样成立,所以flag[i][j] = flag[i-1][j](比较前s[0,i-2]与p[0,j-1]即可)
三、代码实现
def isMatch(self, s: str, p: str) -> bool:
m = len(s)
n = len(p)
# 动态分配数组,注意长度得加一。
flag = [[False for i in range(n+1)] for j in range(m+1)]
for i in range(m+1):
for j in range(n+1):
# 当模式串为空
if j == 0:
# 当字符串为空
if i == 0:
flag[i][j] = True
# 模式串不为空
else:
# 最后一位不为*
if p[j-1] != "*":
# 正常字母比较或者"."匹配任意字符
if (j >=1 and p[j-1]==s[i-1] or p[j-1] == "."): # 这样是否成立就与前段字符串有关
flag[i][j] = flag[i-1][j-1]
# 最后一位为*
else:
# 可能匹配0次,舍弃p[j-1]与p[j-2]
if j >=2:
flag[i][j] |= flag[i][j-2]
# 也可能匹配多次,判断p中"*"前的字符与s最后一个字符是否相同或者p中"*"前的字符为'.'也一样成立,两种情况成功一种即匹配成功
if (j>=2 and i>=1 and (p[j-2] == s[i-1] or p[j-2] =='.')):
flag[i][j] |= flag[i-1][j]
return flag[m][n]
四、效率总结