zoukankan      html  css  js  c++  java
  • 刷题——正则表达式匹配

    首先看题题目

    给你一个字符串 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
    

    如果只是普通匹配,一个一个就好了,但是这题,有.*需要考虑:

    '*' 代表可以匹配零个或多个前面的那一个元素

    任意字符'.'

    那就先暴力一下,嘻嘻嘻

    首先,如果不考虑*,那么问题就会非常简单,先把代码贴出来(因为考虑到一些坑点,先认为当sp都为空是匹配的,当其中一个为空时不匹配,下面的代码有些冗余,不过只是一个样本,之后在此基础上再来考虑本题的*

    初始版本来啦

    (自己在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)

  • 相关阅读:
    最后一次作业-- 总结报告
    第14.15周作业
    第七周作业
    第六周作业
    第四周作业
    第三周作业。
    第四次作业
    第三次作业
    第二次作业
    第一次作业
  • 原文地址:https://www.cnblogs.com/fanwenkeer/p/11259461.html
Copyright © 2011-2022 走看看