代码改变世界

[LeetCode] 10. Regular Expression Matching_Hard tag: backtracking, DP

2021-06-07 20:22  Johnson_强生仔仔  阅读(46)  评论(0编辑  收藏  举报

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

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

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

 

Example 1:

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

Example 2:

Input: s = "aa", p = "a*"
Output: true
Explanation: '*' means zero or more of the preceding element, 'a'. Therefore, by repeating 'a' once, it becomes "aa".

Example 3:

Input: s = "ab", p = ".*"
Output: true
Explanation: ".*" means "zero or more (*) of any character (.)".

Example 4:

Input: s = "aab", p = "c*a*b"
Output: true
Explanation: c can be repeated 0 times, a can be repeated 1 time. Therefore, it matches "aab".

Example 5:

Input: s = "mississippi", p = "mis*is*p*."
Output: false

 

Constraints:

  • 0 <= s.length <= 20
  • 0 <= p.length <= 30
  • s contains only lowercase English letters.
  • p contains only lowercase English letters, '.', and '*'.
  • It is guaranteed for each appearance of the character '*', there will be a previous valid character to match.

 

Idea:

利用recursive, 先看p[0], 如果是{s[0], '.'}, 说明s[0] matches p[0], recursive call. 

如果有*,可以两种情况,也就是所谓的backtracking,可以将p变为p[2:], s 不变,那也就是取0个p[0], 否则的话就可以去掉s[0], 然后p不变,最后取两者的或即可。

时间复杂度和空间复杂度我看solution,然后自己试着去理解时间复杂度。 T: len(s), P: len(p)

Time: O(T + P) * 2^(T + P/2)     我的理解: 最坏的情况,每次都有*, 那么删掉s[0] 或者不删,两种情况,所以对于s来说, 情况有2 ^T 个,同理删掉p的前两个或者不删,情况有2 ^(p/2), 然后每次build,需要(T + P - i - 2j), 那么就O(T + P), 所以O(T + P) * 2^(T) *2 ^( P/2) = O(T + P) * 2^(T + P/2)

Space:  O(T + P) * 2^(T + P/2) 同理

 

Code:

class Solution:
    def isMatch(self, s, p):
        if not p:
            return not s
        first_match = s and p[0] in {s[0], '.'}
        if len(p) >= 2 and p[1] == '*':
            return (self.isMatch(s, p[2:]) or first_match and self.isMatch(s[1:], p))
        else:
            return first_match and self.isMatch(s[1:], p[1:])

 

Idea2: we add cache for first s[:i], p[:j] and save the result in a dictionary, to save the calculation time. Similar like the recursive method above, but we cache the results. 

T: O(T*P)

S: O(T*P)

class Solution:
    def isMatch(self, s, p):
        mem = dict()
        def dp(i, j, s, p, mem):
            # check whether it is done with the p, like not p in recursive method
            if (i, j) not in mem:
                if j == len(p):
                    ans = i == len(s)
                else:
                    first_match = i < len(s) and p[j] in {s[i], '.'}
                    if j + 1 < len(p) and p[j + 1] == '*':
                        ans = dp(i, j + 2, s, p, mem) or first_match and dp(i + 1, j, s, p, mem)
                    else:
                        ans = first_match and dp(i + 1, j + 1, s, p, mem)
                mem[(i, j)] = ans
            return mem[(i, j)]
        return dp(0, 0, s, p, mem)