10. 正则表达式匹配
题目描述
给了一个字符串s和字符规律p
其中p可能含有"."和"","."可以匹配任何单个字符,""需要和前面字符结合,表示匹配0-任意个前面字符
问给定的p能不能匹配s
f1-序列dp |
基本分析
1.怎么定义状态?dp[i][j]表示p的前j个字符能不能匹配s的前i个字符
2.对i分类还是对j分类?j有3种情况,对j分类
- p[j]是字符,要能转移需要有dp[i-1][j-1]能匹配,s[i]=p[j]
- p[j]是".", 要能转移需要有dp[i-1][j-1]能匹配,p[j]是'.'
- p[j]是"*", 需要结合前面一个字符考虑,可能能匹配0个,1个,多个,这个可能是无限的,这个题的难点就在这里
3.怎么推导出来p[j]是"*"的表达式?
- 枚举f[i][j]成立时匹配0、1、2、。。。k时的表达式
- 将i替换为i-1,得到f[i-1][j]时候的表达式
- 分析以上表达式关系,得到f[i][j] = f[i][j-2] 或 (f[i-1][j] 且 p[j-1]能匹配s[i])
4.定义状态的时候有啥细节?
- 预处理:在原字符插空格,(1)为了能直接用i和j表达第几个;(2)可以把f[0][0]=True的结果滚下去
- 开数组:开到[n+1][m+1]
- f[0][0] = True
5.转移的时候的细节?
- 遍历时候,对s是range(0, n+1),需要考虑匹配0;对p是(1,m+1)
- p[j]的后面接"*"时,要一起考虑,需要pass
- p[j]是字符或者"*"时,可以一起考虑,但因为有i-1的转移,需要判断i的越界情况
- p[j]是"*"时,按照转移关系来,需要判断j的越界情况,i的越界情况,p[j-1]能匹配s[i]的情况
代码
class Solution: def isMatch(self, s: str, p: str) -> bool: n, m = len(s), len(p) # 头插空格的原因? s = ' ' + s p = ' ' + p f = [[False] * (m+1) for _ in range(n+1)] f[0][0] = True for i in range(n+1): for j in range(1, m+1): # 如果后面是*表示不能单独使用,跳过 if j+1 <= m and p[j+1]=="*": continue # 考虑为普通字符或者"." if i-1 >=0 and p[j] !="*": f[i][j] = f[i-1][j-1] and (s[i]==p[j] or p[j]==".") elif p[j] == "*": f[i][j] = (j-2>=0 and f[i][j-2]) or (i-1>=0 and f[i-1][j] and (s[i]==p[j-1] or p[j-1]==".")) return f[n][m]
复杂度
时间:共计
空间:
总结
第1个难点是状态定义的写法,需要两个维度i和j,来表示前j匹配前i的情况
第2个难点在于考虑转移时对谁进行分类?这里是j,题目给了3个情况
第3个难点是具体考虑怎么转移?对p[j]取非"*"的时候,按照之前的定义,可以解决了,但是对于"*"的情况,可以匹配0、1、2、...n, 这样写or太长了,怎么处理?题目的做法是用了换元法,先写成[i][j]的情况,再写[i-1][j]的情况,然后发现可以把无限的式子化为有限
第4个难点是状态定义的细节,为了转移f[0][0],用了头插空格
第5个注意点是转移的时候,需要往前去翻,这个时候不管对i还是j,都要先保证不越界
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现