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]

复杂度

时间:共计\(n \cdot m个状态, O(n \cdot m)\)
空间:\(O(n \cdot 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,都要先保证不越界

posted @ 2022-11-08 19:51  zhangk1988  阅读(95)  评论(0)    收藏  举报