LeetCode10. Regular Expression Matching

本文探讨了正则表达式的两种实现方式:递归和动态规划。递归方法虽然直观但效率较低,动态规划方法则更为高效。文章还对比了这两种方法的时间和空间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:

Given an input string (s) and a pattern (p), implement regular expression matching with support for '.' and '*'.

'.' Matches any single character.
'*' Matches zero or more of the preceding element.

The matching should cover the entire input string (not partial).

Note:

  • s could be empty and contains only lowercase letters a-z.
  • p could be empty and contains only lowercase letters a-z, and characters like . or *.

Example 1:

Input:
s = "aa"
p = "a"
Output: false
Explanation: "a" does not match the entire string "aa".

思路一:

可以从模式字符串p来考虑这个问题,情况有三种:

如果p为空,则s为空,匹配成功;s为不空,匹配失败;

如果p的长度为1,或者p的第二个字符不为'*',则只需要考虑p的第一个字母和s的匹配;

如果p的第二个字符为'*',则需要考虑p的前两个字母的组合对s的消减。

迭代进行着三步,可以得出匹配结果。代码如下:

 1 class Solution(object):
 2     def isMatch(self, s, p):
 3         if not p:
 4             return not s
 5         if len(p) == 1 or p[1] != '*':
 6             if not s or (p[0] != s[0] and p[0] != '.'):
 7                 return False
 8             else:
 9                 return self.isMatch(s[1:], p[1:])
10         else:
11             i, length = -1, len(s)
12             while i < length and (i < 0 or s[i] == p[0] or p[0] == '.'):
13                 if self.isMatch(s[i + 1:], p[2:]):
14                     return True
15                 i += 1
16             return False

假设s的长度为n,p的长度为m。考虑到第三中情况下的多种可能性,时间复杂度最好为O(min(m, n))即没有'*',大家冲上来一通匹配,其中一个匹配到头,问题结束,所需的时间复杂度是线性的;最坏的情况是p中‘*’符号很多,我们要一个一个去跳着匹配s中的字符,而每一跳又会生出多种可能性,这可以转化为“减绳子求最大乘积问题”,对应的时间复杂度为O(3^(n/3))。可以看到,这种解法的时间复杂度可以很高。空间复杂度为O(min(n^2, m^2)).在LeetCode的测试用例上,这种解决方案耗时1797ms。

思路二:

考虑用动态规划解决这个问题,以节省时间。假设我们已经知道s中[0, 1,  2, ..., i - 2]和p中[0, 1, 2, ..., j - 2]是否匹配,接下来我们考虑加入s[i - 1]和p[j - 1]后的关系。具体可由p[j - 1]和p[j - 2]的符号类别决定。此种解法的时间复杂度为O(mn),空间复杂度为O(mn)。在时间复杂度上比思路一低很多。此种解法耗时仅97ms。具体代码如下:

class Solution(object):
    def isMatch(self, s, p):
        dp = [[False for _j in range(len(p) + 1)] for _i in range(len(s) + 1)]
        dp[0][0] = True
        for cj in range(2, len(p) + 1):
            if p[cj - 1] == '*':
                dp[0][cj] = dp[0][cj - 2]
        for ri in range(1, len(s) + 1):
            for cj in range(1, len(p) + 1):
                if p[cj - 1] == '.':
                    dp[ri][cj] = dp[ri - 1][cj - 1]
                elif p[cj - 1] == '*':
                    if p[cj - 2] == s[ri - 1] or p[cj - 2] == '.':
                        dp[ri][cj] = dp[ri - 1][cj] or dp[ri - 1][cj - 2] or dp[ri][cj - 2]
                    else:
                        dp[ri][cj] = dp[ri][cj - 2]
                else:
                    dp[ri][cj] = s[ri - 1] == p[cj - 1] and dp[ri - 1][cj - 1]
        return dp[len(s)][len(p)]

 

转载于:https://www.cnblogs.com/plank/p/9166899.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值