一、题目描述:
给你一个字符串 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
解释:“.*” 表示可匹配零个或多个(‘*’)任意字符(‘.’)。
- 提示:
- 1 <= s.length <= 20
- 1 <= p.length <= 30
- s 只包含从 a-z 的小写字母。
- p 只包含从 a-z 的小写字母,以及字符 . 和 *。
- 保证每次出现字符 * 时,前面都匹配到有效的字符
二、解决思路和代码
思路:动态规划
状态转移方程讲解
举例:字符串 s = a a a b c a a b s = aaabcaab s=aaabcaab 和一个字符规律 p = a ∗ b . ∗ b p = a*b.*b p=a∗b.∗b,实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。
现在我们给出一个表格 d p dp dp 记录每个状态下,字符串的匹配情况。表格中第 i i i 行,第 j j j 列中元素 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示的含义是: s 的前 i 个字符 (s[:i]) 与 p 的前 j 个字符 (p[:j]) 是否匹配。若匹配 dp[i][j]=‘T’, 不匹配 dp[i][j]=‘F’。
1. 初始化dp
-
dp[:][0] = [T, F, F, F, F, F, F, F, F]
-
dp[0][1:]
-
由上, dp 初始表格为:
2. 逐行求dp
-
i = 1 i = 1 i=1 , s[:1] = ‘a’
-
dp表格更新过程如下:
-
所以,dp[1][1:] = [T, T, F, F, F, F]。更新 dp 表格:
-
-
i = 2 i = 2 i=2 , s[:2] = ‘aa’
-
dp表格更新过程如下:
-
所以,dp[2][1:] = [F, T, F, F, F, F]。更新 dp 表格:
-
-
i = 3 i = 3 i=3 , s[:3] = ‘aaa’
-
dp表格更新过程如下:
-
所以,dp[3][1:] = [F, T, F, F, F, F]。更新 dp 表格:
-
-
i = 4 i = 4 i=4 , 自己动手画一下吧。。。
3. 总结归纳
-
根据逐行求解 dp 过程,得:
- 当
p
[
j
]
≠
s
[
i
]
and
p
[
j
]
≠
"."
p[j]\ne s[i] \text{ and } p[j] \ne \text{ "." }
p[j]=s[i] and p[j]= "." 时,
d p [ i ] [ j ] = F a l s e dp[i][j] = False dp[i][j]=False - 当
p
[
j
]
=
s
[
i
]
or
p
[
j
]
=
’.’
p[j]=s[i] \text{ or } p[j] = \text{ '.' }
p[j]=s[i] or p[j]= ’.’ 时,
d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] dp[i][j] = dp[i-1][j-1] dp[i][j]=dp[i−1][j−1] - 当
p
[
j
]
=
’*’
p[j] = \text{'*'}
p[j]=’*’ 时,
-
(1) 对于 d p [ i ] [ j ] dp[i][j] dp[i][j],观察上述 s [ : 3 ] s[:3] s[:3] 与 p [ : 2 ] p[:2] p[:2] 的匹配过程,可以发现:
d p [ i ] [ j ] = d p [ i − 0 ] [ j − 2 ] a n d ( p [ j − 1 ] ≠ s [ i − 0 ] a n d p [ j − 1 ] ≠ ’.’ ) o r d p [ i − 0 ] [ j − 2 ] a n d ( p [ j − 1 ] = s [ i − 0 ] o r p [ j − 2 ] = ’.’ ) o r d p [ i − 1 ] [ j − 2 ] a n d ( p [ j − 2 ] = s [ i − 1 ] o r p [ j − 2 ] = ’.’ ) o r d p [ i − 2 ] [ j − 2 ] a n d ( p [ j − 2 ] = s [ i − 2 ] o r p [ j − 2 ] = ’.’ ) o r ...... d p [ i − k ] [ j − 2 ] a n d ( p [ j − 2 ] = s [ i − k ] o r p [ j − 2 ] = ’.’ ) \begin{alignat}{2} dp[i][j] & = dp[i-0][j-2]\ and\ \left ( p[j-1]\ne s[i-0]\ and\ p[j-1]\ne \text{'.'} \right ) \ \mathbf{or} \\ & \quad \ {\color{Red} dp[i-0][j-2]\ and\ \left ( p[j-1]=s[i-0]\ or\ p[j-2]=\text{'.'} \right ) \ \mathbf{or}} \\ & \quad \ {\color{Green} dp[i-1][j-2]\ and\ \left ( p[j-2]=s[i-1]\ or\ p[j-2]=\text{'.'} \right ) \ \mathbf{or}} \\ & \quad \ {\color{Green} dp[i-2][j-2]\ and\ \left ( p[j-2]=s[i-2]\ or\ p[j-2]=\text{'.'} \right ) \ \mathbf{or}} \\ & \quad \ {\color{Green} \text{ ...... }} \\ & \quad \ {\color{Green} dp[i-k][j-2]\ and\ \left ( p[j-2]=s[i-k]\ or\ p[j-2]=\text{'.'} \right ) } \end{alignat} dp[i][j]=dp[i−0][j−2] and (p[j−1]=s[i−0] and p[j−1]=’.’) or dp[i−0][j−2] and (p[j−1]=s[i−0] or p[j−2]=’.’) or dp[i−1][j−2] and (p[j−2]=s[i−1] or p[j−2]=’.’) or dp[i−2][j−2] and (p[j−2]=s[i−2] or p[j−2]=’.’) or ...... dp[i−k][j−2] and (p[j−2]=s[i−k] or p[j−2]=’.’) -
(2) 对于 d p [ i − 1 ] [ j ] dp[i-1][j] dp[i−1][j]:
d p [ i − 1 ] [ j ] = d p [ i − 1 ] [ j − 2 ] a n d ( p [ j − 2 ] ≠ s [ i − 1 ] a n d p [ j − 2 ] ≠ ’.’ ) o r d p [ i − 1 ] [ j − 2 ] a n d ( p [ j − 2 ] = s [ i − 1 ] o r p [ j − 2 ] = ’.’ ) o r d p [ i − 2 ] [ j − 2 ] a n d ( p [ j − 2 ] = s [ i − 2 ] o r p [ j − 2 ] = ’.’ ) o r ...... d p [ i − ( k − 1 ) ] [ j − 2 ] a n d ( p [ j − 2 ] = s [ i − ( k − 1 ) ] o r p [ j − 2 ] = ’.’ ) \begin{alignat}{2} dp[i-1][j] & = dp[i-1][j-2]\ and\ \left ( p[j-2]\ne s[i-1]\ and\ p[j-2]\ne \text{'.'} \right ) \ \mathbf{or}\\ & \quad \ {\color{Green} dp[i-1][j-2]\ and\ \left ( p[j-2]=s[i-1]\ or\ p[j-2]=\text{'.'} \right ) \ \mathbf{or}} \\ & \quad \ {\color{Green} dp[i-2][j-2]\ and\ \left ( p[j-2]=s[i-2]\ or\ p[j-2]=\text{'.'} \right ) \ \mathbf{or}} \\ & \quad \ {\color{Green} \text{ ...... }} \\ & \quad \ {\color{Green} dp[i-\left ( k-1 \right ) ][j-2]\ and\ \left ( p[j-2]=s[i-\left ( k-1 \right ) ]\ or\ p[j-2]=\text{'.'} \right ) } \end{alignat} dp[i−1][j]=dp[i−1][j−2] and (p[j−2]=s[i−1] and p[j−2]=’.’) or dp[i−1][j−2] and (p[j−2]=s[i−1] or p[j−2]=’.’) or dp[i−2][j−2] and (p[j−2]=s[i−2] or p[j−2]=’.’) or ...... dp[i−(k−1)][j−2] and (p[j−2]=s[i−(k−1)] or p[j−2]=’.’) -
(3) 由上述 (1) 和 (2) 推到得:
d p [ i ] [ j ] = d p [ i − 0 ] [ j − 2 ] a n d ( p [ j − 1 ] ≠ s [ i ] o r p [ j − 1 ] ≠ ’.’ ) o r d p [ i − 0 ] [ j − 2 ] a n d ( p [ j − 1 ] = s [ i ] o r p [ j − 1 ] = ’.’ ) o r d p [ i − 1 ] [ j ] a n d ( p [ j − 1 ] = s [ i ] o r p [ j − 1 ] = ’.’ ) = d p [ i − 0 ] [ j − 2 ] o r d p [ i − 1 ] [ j ] a n d ( p [ j − 1 ] = s [ i ] o r p [ j − 1 ] = ’.’ ) \begin{alignat}{2} dp[i][j] & = dp[i-0][j-2]\ and\ \left ( p[j-1]\ne s[i]\ or\ p[j-1]\ne \text{'.'} \right ) \ \mathbf{or} \\ & \quad \ {\color{Red} dp[i-0][j-2]\ and\ \left ( p[j-1]=s[i]\ or\ p[j-1]=\text{'.'} \right ) \ \mathbf{or}} \\ & \quad \ {\color{Green} dp[i-1][j]\ and\ \left ( p[j-1]=s[i]\ or\ p[j-1]=\text{'.'} \right )}\\ & = dp[i-0][j-2]\ \mathbf{or} \\ & \quad \ {\color{Green} dp[i-1][j]\ and\ \left ( p[j-1]=s[i]\ or\ p[j-1]=\text{'.'} \right )} \end{alignat} dp[i][j]=dp[i−0][j−2] and (p[j−1]=s[i] or p[j−1]=’.’) or dp[i−0][j−2] and (p[j−1]=s[i] or p[j−1]=’.’) or dp[i−1][j] and (p[j−1]=s[i] or p[j−1]=’.’)=dp[i−0][j−2] or dp[i−1][j] and (p[j−1]=s[i] or p[j−1]=’.’)
这里,可以理解为: d p [ i ] [ j ] = d p [ i ] [ j − 2 ] dp[i][j] = dp[i][j-2] dp[i][j]=dp[i][j−2] 是 零个 * 前面的字符的情况; d p [ i ] [ j ] = d p [ i − 1 ] [ j ] dp[i][j] = dp[i-1][j] dp[i][j]=dp[i−1][j] 是 1个 或者 多个 * 前面的字符的情况。
-
- 当
p
[
j
]
≠
s
[
i
]
and
p
[
j
]
≠
"."
p[j]\ne s[i] \text{ and } p[j] \ne \text{ "." }
p[j]=s[i] and p[j]= "." 时,
-
正则表达式匹配的状态转移方程为:
d p [ i ] [ j ] = { F a l s e , p [ j ] ≠ s [ i ] and p [ j ] ≠ ’.’ d p [ i − 1 ] [ j − 1 ] , p [ j ] = s [ i ] or p [ j ] = ’.’ d p [ i ] [ j − 2 ] o r d p [ i − 1 ] [ j ] a n d ( p [ j − 1 ] = s [ i ] o r p [ j − 1 ] = ′ . ′ ) , p [ j ] = ’*’ dp[i][j] = \begin{cases} False, & p[j]\ne s[i] \text{ and } p[j]\ne \text{'.'}\\ dp[i-1][j-1], & p[j]=s[i] \text{ or } p[j]=\text{'.'}\\ dp[i][j-2]\ \mathbf{or} \ dp[i-1][j]\ and \ (p[j-1]=s[i]\ or\ p[j-1]='.'), & p[j]=\text{'*'}\\ \end{cases} dp[i][j]=⎩ ⎨ ⎧False,dp[i−1][j−1],dp[i][j−2] or dp[i−1][j] and (p[j−1]=s[i] or p[j−1]=′.′),p[j]=s[i] and p[j]=’.’p[j]=s[i] or p[j]=’.’p[j]=’*’
代码
class Solution:
def isMatch(self, s: str, p: str) -> bool:
## 初始化
dp = [[False for i in range(len(p)+1)] for j in range(len(s)+1)]
dp[0][0] = True
for idx in range(1, len(dp[0])):
if p[idx-1] == '*' and idx-2>=0:
dp[0][idx] = dp[0][idx-2]
## 根据状态转移方程更新dp
for i in range(1, len(dp)):
for j in range(1, len(dp[0])):
if p[j-1]==s[i-1] or p[j-1]=='.':
dp[i][j] = dp[i-1][j-1]
if p[j-1]=='*':
dp[i][j] = dp[i-1][j] if p[j-2]==s[i-1] or p[j-2]=='.' else dp[i][j-2]
return dp[-1][-1]