同 LeetCode 第 10 题,传送门:10. 正则表达式匹配。
小 Fu:视频讲解。
Java 写法:
image-20190124231323024Python 写法:
class Solution(object):
def isMatch(self, s, p):
"""
:type s: str
:type p: str
:rtype: bool
"""
n = len(s)
m = len(p)
dp = [[False for _ in range(m + 1)] for _ in range(n + 1)]
# 当 s 和 p 的长度都为 0 的时候,定义成匹配
dp[0][0] = True
# 处理一种特例
for i in range(2, m + 1):
if p[i - 1] == '*' and dp[0][i - 2]:
dp[0][i] = True
for i in range(1, n + 1):
for j in range(1, m + 1):
if s[i - 1] == p[j - 1] or p[j - 1] == '.':
dp[i][j] = dp[i - 1][j - 1]
elif p[j - 1] == '*':
# 这是最麻烦的情况
if p[j - 2] != s[i - 1] and p[j - 2] != '.':
dp[i][j] = dp[i][j - 2]
else:
dp[i][j] = dp[i][j - 2] or dp[i][j - 1] or dp[i - 1][j]
return dp[n][m]
参考资料:http://www.voidcn.com/article/p-zioiffqq-mm.html
“大雪菜”的思路:
思路分析:动态规划,时间复杂度为 。
状态表示:f[i][j]
表示 p
从 j
开始到结尾,是否能匹配 s
从 i
开始到结尾。
状态转移:
(关键看后面一个字符,所以,动态规划,从后向前做)
1、如果 p[j+1]
不是通配符 '*'
,则 f[i][j]
是真,当且仅当 s[i]
可以和 p[j]
匹配,且 f[i+1][j+1]
是真;
说明:这一点比价好理解。
2、如果 p[j+1]
是通配符 '*'
,则下面的情况只要有一种满足,f[i][j]
就是真;
(1)f[i][j+2]
是真;
(2)s[i]
可以和 p[j]
匹配,且 f[i+1][j]
是真;
第 种情况下的状态转移很好理解,那第 种情况下的状态转移怎么理解呢?
最直观的转移方式是这样的:枚举通配符 '*'
可以匹配多少个 p[j]
,只要有一种情况可以匹配,则 f[i][j]
就是真;
这样做的话,我们发现,f[i][j]
除了枚举 个 p[j]
之外,其余的枚举操作都包含在 f[i+1][j]
中了,所以我们只需判断
f[i+1][j]
是否为真,以及 s[i]
是否可以和 p[j]
匹配即可。
时间复杂度分析: 表示 的长度, 表示 的长度,总共 个状态,状态转移复杂度 ,所以总时间复杂度是 。
Python 写法:
class Solution(object):
def isMatch(self, s, p):
"""
:type s: str
:type p: str
:rtype: bool
"""
n = len(s)
m = len(p)
f = [[-1 for _ in range(m + 1)] for _ in range(n + 1)]
return self.dp(0, 0, s, p, f, n, m)
def dp(self, x, y, s, p, f, n, m):
if f[x][y] != -1:
return f[x][y]
if y == m:
# 如果模式串用完了,看看字符串是不是也用完了
f[x][y] = (x == n)
return f[x][y]
first_match = x < n and (s[x] == p[y] or p[y] == '.')
if y + 1 < m and p[y + 1] == '*':
res = self.dp(x, y + 2, s, p, f, n, m) or self.dp(x + 1, y, s, p, f, n, m)
else:
res = first_match and self.dp(x + 1, y + 1, s, p, f, n, m)
f[x][y] = res
return f[x][y]
(完)
网友评论