LeetCode题目(Python实现): 通配符匹配

题目

给定一个字符串 (s) 和一个字符模式 (p) ,实现一个支持 '?''*' 的通配符匹配。

'?' 可以匹配任何单个字符。
'*' 可以匹配任意字符串(包括空字符串)。

两个字符串完全匹配才算匹配成功。

说明:

  • s 可能为空,且只包含从 a-z 的小写字母。
  • p 可能为空,且只包含从 a-z 的小写字母,以及字符 ?*

示例1

输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。

示例2

输入:
s = "aa"
p = "*"
输出: true
解释: '*' 可以匹配任意字符串。

示例3

输入:
s = "cb"
p = "?a"
输出: false
解释: '?' 可以匹配 'c', 但第二个 'a' 无法匹配 'b'

示例4

输入:
s = "adceb"
p = "*a*b"
输出: true
解释: 第一个 '*' 可以匹配空字符串, 第二个 '*' 可以匹配字符串 "dce".

示例5

输入:
s = "acdcb"
p = "a*c?b"
输入: false

优化递归

算法实现

def remove_duplicate_stars(self, p):
    if p == '':
        return p
    p1 = [p[0], ]
    for x in p[1:]:
        if p1[-1] != '*' or p1[-1] == '*' and x != '*':
            p1.append(x)
    return ''.join(p1)

def helper(self, s, p):
    dp = self.dp
    if (s, p) in dp:
        return dp[(s, p)]

    if p == s or p == '*':
        dp[(s, p)] = True
    elif p == '' or s == '':
        dp[(s, p)] = False
    elif p[0] == s[0] or p[0] == '?':
        dp[(s, p)] = self.helper(s[1:], p[1:])
    elif p[0] == '*':
        dp[(s, p)] = self.helper(s, p[1:]) or self.helper(s[1:], p)
    else:
        dp[(s, p)] = False

    return dp[(s, p)]

def isMatch(self, s, p):
    p = self.remove_duplicate_stars(p)
    # memorization hashmap to be used during the recursion
    self.dp = {}
    return self.helper(s, p)

执行结果

执行结果 : 通过
执行用时 : 1244 ms, 在所有 Python3 提交中击败了18.32%的用户
内存消耗 : 664.2 MB, 在所有 Python3 提交中击败了5.51%的用户

在这里插入图片描述

复杂度分析

  • 时间复杂度:最好的情况下 O ( min ⁡ ( S , P ) ) \mathcal{O}(\min(S, P)) O(min(S,P)),最坏的情况下是 O ( 2 min ⁡ ( S , P / 2 ) ) \mathcal{O}(2^{\min(S, P/2)}) O(2min(S,P/2))。其中 SS 和 PP 指的是输入字符串和字符模式的长度。 最好的情况很明显,让我们估算最坏的情况。最耗时的递归是字符模式上的星号形成树的情况,将执行两个分支 helper(s, p[1:]) 和 helper(s[1:], p)。数据清理后字符模式中的最大星树为 P / 2 P/2 P/2,因此时间复杂度为 O ( 2 min ⁡ ( S , P / 2 ) ) \mathcal{O}(2^{\min(S, P/2)}) O(2min(S,P/2))
  • 空间复杂度: O ( 2 min ⁡ ( S , P / 2 ) ) \mathcal{O}(2^{\min(S, P/2)}) O(2min(S,P/2)),用来存储记忆哈希表和递归调用堆栈。

动态规划

算法实现

def isMatch(self, s, p):
    s_len = len(s)
    p_len = len(p)

    # base cases
    if p == s or p == '*':
        return True
    if p == '' or s == '':
        return False

    # init all matrix except [0][0] element as False
    d = [[False] * (s_len + 1) for _ in range(p_len + 1)]
    d[0][0] = True

    # DP compute
    for p_idx in range(1, p_len + 1):
        # the current character in the pattern is '*'
        if p[p_idx - 1] == '*':
            s_idx = 1
            # d[p_idx - 1][s_idx - 1] is a string-pattern match
            # on the previous step, i.e. one character before.
            # Find the first idx in string with the previous math.
            while not d[p_idx - 1][s_idx - 1] and s_idx < s_len + 1:
                s_idx += 1
            # If (string) matches (pattern),
            # when (string) matches (pattern)* as well
            d[p_idx][s_idx - 1] = d[p_idx - 1][s_idx - 1]
            # If (string) matches (pattern),
            # when (string)(whatever_characters) matches (pattern)* as well
            while s_idx < s_len + 1:
                d[p_idx][s_idx] = True
                s_idx += 1
        # the current character in the pattern is '?'
        elif p[p_idx - 1] == '?':
            for s_idx in range(1, s_len + 1):
                d[p_idx][s_idx] = d[p_idx - 1][s_idx - 1]
                # the current character in the pattern is not '*' or '?'
        else:
            for s_idx in range(1, s_len + 1):
                # Match is possible if there is a previous match
                # and current characters are the same
                d[p_idx][s_idx] = \
                    d[p_idx - 1][s_idx - 1] and p[p_idx - 1] == s[s_idx - 1]

    return d[p_len][s_len]

执行结果

在这里插入图片描述

复杂度分析

在这里插入图片描述

回溯

算法实现

def isMatch(self, s, p):
    """
    :type s: str
    :type p: str
    :rtype: bool
    """
    s_len, p_len = len(s), len(p)
    s_idx = p_idx = 0
    star_idx = s_tmp_idx = -1

    while s_idx < s_len:
        # If the pattern caracter = string character
        # or pattern character = '?'
        if p_idx < p_len and p[p_idx] in ['?', s[s_idx]]:
            s_idx += 1
            p_idx += 1
        # If pattern character = '*'
        elif p_idx < p_len and p[p_idx] == '*':
            # Check the situation
            # when '*' matches no characters
            star_idx = p_idx
            s_tmp_idx = s_idx
            p_idx += 1
        # If pattern character != string character
        # or pattern is used up
        # and there was no '*' character in pattern
        elif star_idx == -1:
            return False
        # If pattern character != string character
        # or pattern is used up
        # and there was '*' character in pattern before
        else:
            # Backtrack: check the situation
            # when '*' matches one more character
            p_idx = star_idx + 1
            s_idx = s_tmp_idx + 1
            s_tmp_idx = s_idx

    # The remaining characters in the pattern should all be '*' characters
    return all(x == '*' for x in p[p_idx:])

执行结果

在这里插入图片描述

复杂度分析

  • 时间复杂度:最好的情况下是 O ( min ⁡ ( S , P ) ) ) \mathcal{O}(\min(S, P))) O(min(S,P))),平均情况下是 O ( S log ⁡ P ) \mathcal{O}(S \log P) O(SlogP),其中 S S S P P P 指的是字符模式和输入字符串的长度。
  • 空间复杂度: O ( 1 ) \mathcal{O}(1) O(1)

小结

只想说一句,好难啊啊啊啊~

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值