Advertisement

Regular Expression Matching - LeetCode 10 Solution

Regular Expression Matching - Complete Solution Guide

Regular Expression Matching is LeetCode problem 10, a Hard level challenge. This complete guide provides step-by-step explanations, multiple solution approaches, and optimized code in python3, java, cpp, c.

Problem Statement

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'. Therefo

Detailed Explanation

The problem asks us to implement regular expression matching with support for two special characters: '.' and '*'. The '.' character matches any single character, and the '*' character matches zero or more occurrences of the preceding character. The matching should cover the entire input string, meaning a partial match is not sufficient. We are given an input string 's' and a pattern 'p', and we must return true if the pattern matches the string, and false otherwise.

Solution Approach

The provided code uses a top-down dynamic programming approach with memoization to solve the regular expression matching problem. A recursive function `dp(i, j)` is defined to determine if the substring of `s` starting at index `i` matches the substring of `p` starting at index `j`. The memoization table `memo` stores the results of previous calls to `dp(i, j)` to avoid redundant computations. The function handles base cases where either the string or pattern (or both) is exhausted, as well as the two cases for the '*' character.

Step-by-Step Algorithm

  1. Step 1: Define a recursive function `dp(i, j)` that takes the starting indices `i` for the string `s` and `j` for the pattern `p` as input. This function returns `true` if `s[i:]` matches `p[j:]` and `false` otherwise.
  2. Step 2: Check the base case: if `j` reaches the end of `p`, then the match is successful only if `i` also reaches the end of `s`.
  3. Step 3: Check if the result for the current `(i, j)` pair is already memoized in the `memo` table. If so, return the stored value.
  4. Step 4: Determine if the first characters of the current substrings match. This is true if `s[i]` and `p[j]` are equal, or if `p[j]` is a '.', and `i` is within the bounds of `s`.
  5. Step 5: If the next character in `p` after `p[j]` is a '*', handle the two cases it represents:
  6. - Case 1: The '*' matches zero occurrences of the preceding character. In this case, skip the 'char*' part of the pattern and recursively call `dp(i, j + 2)`.
  7. - Case 2: The '*' matches one or more occurrences of the preceding character. This requires a `first_match` and then advance in the string while staying at the same pattern index `dp(i + 1, j)`.
  8. - The result is true if either of these cases is true.
  9. Step 6: If the next character in `p` is not a '*', then it's a standard one-to-one character match. The match is successful only if `first_match` is true, and the remaining substrings also match. Recursively call `dp(i + 1, j + 1)`.
  10. Step 7: Store the result in the `memo` table before returning it.
  11. Step 8: The initial call to `dp(0, 0)` starts the matching process from the beginning of both `s` and `p`.

Key Insights

  • Insight 1: The core idea is to use dynamic programming to store and reuse results of subproblems, which greatly improves efficiency compared to a naive recursive approach.
  • Insight 2: The '*' character presents two main scenarios: it can match zero occurrences of the preceding character (effectively skipping that part of the pattern), or it can match one or more occurrences (consuming characters from the string and keeping the same position in the pattern).
  • Insight 3: Memoization is crucial for optimizing the recursive solution. Without it, the same subproblems are repeatedly solved, leading to exponential time complexity. The problem constraints on string length (up to 20) also hint towards a dynamic programming solution, as a purely recursive approach would likely time out.

Complexity Analysis

Time Complexity: O(mn)

Space Complexity: O(mn)

Topics

This problem involves: String, Dynamic Programming, Recursion.

Companies

Asked at: Airbnb, Amazon, Apple, Bloomberg, ByteDance, Citadel, Confluent, Coupang, Hiver, MakeMyTrip, Meta, Microsoft, Oracle, Snowflake, TikTok, Turing, Uber, WinZO, WorldQuant, X, Yahoo, Zoho.