【LeetCode】10.Regular Expression Matching(Python)

本文深入探讨了LeetCode上正则表达式匹配难题的解决方案,详细解析了递归和动态规划两种方法,旨在帮助读者理解和掌握正则表达式的高级应用。

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

Problem

LeetCode 10: 正则表达式匹配
难度:hard

给定输入字符串和模式(P),实现与“.”和“*”支持匹配的正则表达式。

  • “.”匹配任何单个字符。
  • “*”与前面的单个元素零个或多个匹配。

匹配应该覆盖整个输入字符串(而不是部分)。

注:

  • s可以为空,并且只包含小写字母a-z。
  • p可以为空,只包含小写字母a-z和类似的字符。或者*。

例1:
输入:S=“AA”, P=“A”
输出:假
说明:"a”与整个字符串“a a”不匹配。

例2:
输入:S=“AA”, P=“a*”
输出:真
解释:"*”表示前面元素“a”的零个或多个。因此,重复“a”一次,它就变成“aa”。


Algorithmic thinking

根据条件进行截取拆分,通过递归循环。


Python3 submission


class Solution:
    def isMatch(self, s, p):
        """ s:str, p:str"""
        def dfs(s_idx, p_idx, memo):
            if (s_idx, p_idx) in memo:
                return memo[(s_idx, p_idx)]

            if p_idx >= len(p):
                return s_idx == len(s)

            cur_match = s_idx < len(s) and (
                s[s_idx] == p[p_idx] or p[p_idx] == "."
            )

            if p_idx + 1 < len(p) and p[p_idx+1] == "*":
                match = dfs(s_idx, p_idx+2, memo) or \
                        (cur_match and dfs(s_idx+1, p_idx, memo))

            else:
                match = cur_match and dfs(s_idx+1, p_idx+1, memo)

            memo[(s_idx, p_idx)] = match

            return match
        return dfs(0, 0, {})


if __name__ == '__main__':
    str1 = '123321312s'
    p1 = '123.*s'

    result = Solution().isMatch(str1, p1)
    print(result)


UnitTest solution

相关文档:
https://leetcode.com/problems/regular-expression-matching/discuss/5723/My-DP-approach-in-Python-with-comments-and-unittest

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import unittest
"""
单元测试版,注意模块命名这里不能用'.'
"""


class Solution(object):
    def isMatch(self, s, p):
        # The DP table and the string s and p use the same indexes i and j, but
        # table[i][j] means the match status between p[:i] and s[:j], i.e.
        # table[0][0] means the match status of two empty strings, and
        # table[1][1] means the match status of p[0] and s[0]. Therefore, when
        # referring to the i-th and the j-th characters of p and s for updating
        # table[i][j], we use p[i - 1] and s[j - 1].

        # Initialize the table with False. The first row is satisfied.
        table = [[False] * (len(s) + 1) for _ in range(len(p) + 1)]

        # Update the corner case of matching two empty strings.
        table[0][0] = True

        # Update the corner case of when s is an empty string but p is not.
        # Since each '*' can eliminate the charter before it, the table is
        # vertically updated by the one before previous. [test_symbol_0]
        for i in range(2, len(p) + 1):
            table[i][0] = table[i - 2][0] and p[i - 1] == '*'

        for i in range(1, len(p) + 1):
            for j in range(1, len(s) + 1):
                if p[i - 1] != "*":
                    # Update the table by referring the diagonal element.
                    table[i][j] = table[i - 1][j - 1] and \
                                  (p[i - 1] == s[j - 1] or p[i - 1] == '.')
                else:
                    # Eliminations (referring to the vertical element)
                    # Either refer to the one before previous or the previous.
                    # I.e. * eliminate the previous or count the previous.
                    # [test_symbol_1]
                    table[i][j] = table[i - 2][j] or table[i - 1][j]

                    # Propagation (referring to the horizontal element)
                    # If p's previous one is equal to the current s, with
                    # helps of *, the status can be propagated from the left.
                    # [test_symbol_2]
                    if p[i - 2] == s[j - 1] or p[i - 2] == '.':
                        table[i][j] |= table[i][j - 1]

        return table[-1][-1]


class TestSolution(unittest.TestCase):
    def test_none_0(self):
        s = ""
        p = ""
        self.assertTrue(Solution().isMatch(s, p))

    def test_none_1(self):
        s = ""
        p = "a"
        self.assertFalse(Solution().isMatch(s, p))

    def test_no_symbol_equal(self):
        s = "abcd"
        p = "abcd"
        self.assertTrue(Solution().isMatch(s, p))

    def test_no_symbol_not_equal_0(self):
        s = "abcd"
        p = "efgh"
        self.assertFalse(Solution().isMatch(s, p))

    def test_no_symbol_not_equal_1(self):
        s = "ab"
        p = "abb"
        self.assertFalse(Solution().isMatch(s, p))

    def test_symbol_0(self):
        s = ""
        p = "a*"
        self.assertTrue(Solution().isMatch(s, p))

    def test_symbol_1(self):
        s = "a"
        p = "ab*"
        self.assertTrue(Solution().isMatch(s, p))

    def test_symbol_2(self):
        # E.g.
        #   s a b b
        # p 1 0 0 0
        # a 0 1 0 0
        # b 0 0 1 0
        # * 0 1 1 1
        s = "abb"
        p = "ab*"
        self.assertTrue(Solution().isMatch(s, p))


if __name__ == "__main__":
    unittest.main()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值