101 Logo
onenoughtone

Solution Approach

Solution Overview

There are 2 main approaches to solve this problem:

  1. Recursive Approach with Memoization - Complex approach
  2. Dynamic Programming Approach - Complex approach

Approach 1: Recursive Approach with Memoization

The recursive approach breaks down the problem by considering one character at a time from both the text and pattern. We handle different cases based on the current characters and use memoization to avoid redundant calculations.

The key insight is to handle the '*' character separately. When we encounter a '*', we have two choices:

  1. Ignore the character and its preceding element (match zero occurrences)
  2. Match the current character in the text with the preceding element in the pattern and continue matching

Algorithm:

  1. Create a recursive function that takes the current positions in the text and pattern.
  2. Base case: If we've reached the end of the pattern, return true if we've also reached the end of the text.
  3. If the next character in the pattern is followed by '*', we have two options:
  4. a. Skip the pattern character and the '*' (match zero occurrences).
  5. b. If the current characters match, try matching the current text character with the pattern character and continue.
  6. If the current characters match (either exact match or '.'), move to the next characters in both strings.
  7. Use memoization to store results of subproblems to avoid redundant calculations.
  8. Return false if none of the above conditions are met.

Time Complexity:

O(m × n)

Where m is the length of the text and n is the length of the pattern. With memoization, we solve each subproblem only once, and there are m × n possible subproblems.

Space Complexity:

O(m × n)

We need O(m × n) space for the memoization table to store results for all possible subproblems. The recursion stack also uses O(m + n) space in the worst case.

Approach 2: Dynamic Programming Approach

We can solve this problem using a bottom-up dynamic programming approach. We'll create a 2D DP table where dp[i][j] represents whether the first i characters of the text match the first j characters of the pattern.

The key is to build the table incrementally, starting from empty strings and working our way up to the full strings.

Algorithm:

  1. Create a 2D DP table of size (m+1) × (n+1), where m is the length of the text and n is the length of the pattern.
  2. Initialize dp[0][0] = true (empty pattern matches empty text).
  3. For patterns like 'a*', 'b*', etc., which can match empty strings, set dp[0][j] accordingly.
  4. For each cell dp[i][j], consider the following cases:
  5. a. If pattern[j-1] is '*', we have two options:
  6. i. Ignore the pattern character and its preceding element (dp[i][j-2]).
  7. ii. If the current text character matches the preceding pattern character, use the result from dp[i-1][j].
  8. b. If the current characters match (either exact match or '.'), use the result from dp[i-1][j-1].
  9. Return dp[m][n], which represents whether the entire text matches the entire pattern.

Time Complexity:

O(m × n)

Where m is the length of the text and n is the length of the pattern. We fill in a table of size m × n, and each cell takes constant time to compute.

Space Complexity:

O(m × n)

We need a 2D DP table of size (m+1) × (n+1) to store the intermediate results.

Pseudocode

solution.pseudo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function isMatch(text, pattern):
// Create a memoization map
memo = {}
function match(i, j):
// Check if we've already computed this result
if (i, j) in memo:
return memo[(i, j)]
// Base case: if we've reached the end of the pattern
if j == length of pattern:
return i == length of text
// Check if the current characters match
firstMatch = i < length of text and (pattern[j] == text[i] or pattern[j] == &apos;.&apos;)
// If the next character is &apos;*&apos;
if j + 1 < length of pattern and pattern[j + 1] == &apos;*&apos;:
// Either skip the pattern character and &apos;*&apos; (match zero occurrences)
// or match the current character and continue with the same pattern
result = match(i, j + 2) or (firstMatch and match(i + 1, j))
else:
// If the current characters match, move to the next characters
result = firstMatch and match(i + 1, j + 1)
// Store the result in the memoization map
memo[(i, j)] = result
return result
return match(0, 0)
ProblemSolutionCode
101 Logo
onenoughtone