首先看题题目
给你一个字符串 s
和一个字符规律 p
,请你来实现一个支持 '.'
和 '*'
的正则表达式匹配。
'.' 匹配任意单个字符
'*' 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s
的,而不是部分字符串。
说明:
s
可能为空,且只包含从a-z
的小写字母。p
可能为空,且只包含从a-z
的小写字母,以及字符.
和*
。
示例 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
如果只是普通匹配,一个一个就好了,但是这题,有.
和*
需要考虑:
'*' 代表可以匹配零个或多个前面的那一个元素
任意字符'.'
那就先暴力一下,嘻嘻嘻
首先,如果不考虑*
,那么问题就会非常简单,先把代码贴出来(因为考虑到一些坑点,先认为当s
和p
都为空是匹配的,当其中一个为空时不匹配,下面的代码有些冗余,不过只是一个样本,之后在此基础上再来考虑本题的*
)
初始版本来啦
(自己在Pythoncharm上面打的)
def ismatch(s, p):
m = len(s)
n = len(p)
if m == 0 and n == 0:
return "true"
if m == 0 or n == 0:
return "false"
if m != n:
return "false"
i = 0
while i < m:
if s[i] != p[i] and p[i] != '.' and s[i] != '.':
return "flase"
else:
i += 1
return "true"
def run():
s = input("输入s:")
p = input("输入p:")
print(ismatch(s,p))
run()
得到初始版本之后,将其转化成递归方法
def recuring_ismatch(s, p) -> bool:
if not p:
return not s
flag = bool(s) and p[0] in {'.', s[0]}
return flag and recuring_ismatch(s[1:], p[1:])
这样子看起来额就舒服很多了
接下来我们就要考虑到*
了
在进入递归之前我们就要考虑到下一个字符是不是*
,如果是的就有两种情况:
如果flag为1(能够匹配),那么s往后移1个。
如果flag为0(不能匹配,即为0次),那么p往后移动2个
核心代码
def recurng_ismatch(s, p) -> bool:
if not p:
return not s
flag = bool(s) and p[0] in {'.', s[0]}
if len(p) >= 2 and p[1] == '*':
return (flag and recuring_ismacth(s[1:], p)) or recuring_ismatch(s, p[2:])
else:
return recuring_ismatch(s[1:], p[1:])
有了递归的基础之后,在这之上继续做优化(动态规划)
用python字典(dp[(i,j)])
创建demo字典用来存放
class Solution:
def isMatch(self, s: str, p: str) -> bool:
demo = dict()
def dp(i,j):
if (i,j) in demo:
return demo[(i,j)]
if j == len(p):
return i == len(s)
flag = i < len(s) and p[j] in {'.', s[i]}
if j+2 <= len(p) and p[j+1] == '*':
ans = (flag and dp(i+1,j)) or dp(i,j+2)
else:
ans = flag and dp(i+1,j+1)
demo[(i,j)] = ans
return ans
return dp(0,0)
s = ''
p = ''
sol = Solution()
flag = sol.isMatch(s, p)
if flag == 0:
print("false")
else:
print("true")
现在速度有了提升
如果不用递归来做的话,也是可以的
dp[i][j]
表示s的前i个字符和p的前j个字符是否能够成功匹配
先对第一列做初始化的处理
for i in range(n):
if p[i] == '*' and dp[0][i - 1]:
dp[0][i + 1] = dp[0][i - 1]
当s[i]==p[j] or p[j]=='.'
时向前移动一位,dp[i+1][j+1]=dp[i][j]
当p[j]=='*'
时,需要考虑p[j-1]
是否与s[i]
相等或者s[i]=='.'
,
如果满足这种情况:
分两种情况考虑,第一是匹配0次,j往后移动两位,第二是匹配成功,i往后移动一位
dp[i+1][j+1] = dp[i][j+1] or dp[i+1][j-1]
如果不满足这种情况即匹配0次,j往后移动两位
class Solution(object):
def isMatch(self, s, p):
m, n = len(s), len(p)
dp = [[0 for _ in range(n + 1)] for _ in range(m + 1)]
dp[0][0] = 1
for i in range(n):
if p[i] == '*' and dp[0][i - 1]:
dp[0][i + 1] = dp[0][i - 1]
for i in range(m):
for j in range(n):
# 如果相等 或 p[j]是. 的时候向前匹配
if s[i] == p[j] or p[j] == '.':
dp[i + 1][j + 1] = dp[i][j]
# 如果p[j-1]是* 匹配成功则i向前移动1,*前面匹配不成功则j移动2
elif p[j] == '*':
if s[i] == p[j - 1] or p[j - 1] == '.':
dp[i + 1][j + 1] = dp[i + 1][j - 1] or dp[i][j + 1]
else:
dp[i + 1][j + 1] = dp[i + 1][j - 1]
return dp[-1][-1]
当然了还有一道题目与这道题目十分的相似
先看题目吧!
给定一个字符串 (s) 和一个字符模式 (p) ,实现一个支持 '?' 和 '*' 的通配符匹配。
'?' 可以匹配任何单个字符。
'*' 可以匹配任意字符串(包括空字符串)。
两个字符串完全匹配才算匹配成功。
其实我们可以模仿上面的代码去做了,只不过这里*
变成了可以匹配任意字符串,不需要考虑前的字符了
class Solution:
def isMatch(self, s: str, p: str):
m, n = len(s), len(p)
dp = [[0 for _ in range(n + 1)] for _ in range(m + 1)]
dp[0][0] = 1
for i in range(n):
if p[i] == '*':
dp[0][i+1] = dp[0][i]
for i in range(m):
for j in range(n):
if s[i] == p[j] or p[j] == '?':
dp[i+1][j+1] = dp[i][j]
# 要么为空要么匹配,所以要么i往后移动一位,要么j往后移动一位
elif p[j] == '*':
dp[i+1][j+1] = dp[i+1][j] or dp[i][j+1]
return dp[-1][-1]
虽然能控制到1s以内,不过对于这道题有更加省时间的办法
class Solution:
def isMatch(self, s: str, p: str):
m, n = len(s), len(p)
ii = 0
jj = -1
i, j = 0, 0
while i < m:
# 相等时往后移动
if j < n and (s[i] == p[j] or p[j] == '?'):
i += 1
j += 1
# 如果p[j]等于'*'的时候,记录i与j的位置,先匹配0次,j+1
elif j < n and p[j] == '*':
ii = i
jj = j
j += 1
# 这是上述情况不满足,这个时候我们取出上次储存的位置来匹配,i往后移动一位
elif jj != -1:
j = jj+1
ii += 1
i = ii
else:
return False
# 如果p中还有多余的*就当作空字符处理,如果还有其他多余的字符则匹配失败
for i in p[j:]:
if i != '*':
return False
return True
这个速度提升的简直不要太快
不过对于动态规划的解法的确是很难掌握,需要多做一些题目才能掌握这种感觉,还得继续加油。
题解的连接在下方,有兴趣的可以去了解一下嗷
链接:https://leetcode-cn.com/problems/two-sum/solution/ji-yu-guan-fang-ti-jie-gen-xiang-xi-de-jiang-jie-b/
来源:力扣(LeetCode)