101 Logo
onenoughtone

Solution Approach

Solution Overview

There are 3 main approaches to solve this problem:

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

Approach 1: Dynamic Programming Approach

The dynamic programming approach involves creating 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.

We fill this table incrementally, considering different cases based on the current characters in the text and pattern.

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 starting with '*', set dp[0][j] accordingly, as '*' can match an empty string.
  4. For each cell dp[i][j], consider the following cases:
  5. a. If pattern[j-1] is '?', it matches any single character, so dp[i][j] = dp[i-1][j-1].
  6. b. If pattern[j-1] is '*', it can match zero or more characters, so dp[i][j] = dp[i][j-1] || dp[i-1][j].
  7. c. If pattern[j-1] is a regular character, it must match the current character in the text, so dp[i][j] = (text[i-1] == pattern[j-1]) && dp[i-1][j-1].
  8. 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.

Approach 2: Greedy Approach with Backtracking

The greedy approach with backtracking tries to match the pattern character by character, with special handling for the '*' character. When we encounter a '*', we have multiple choices: match zero characters, match one character, match two characters, and so on.

We use a greedy strategy by initially trying to match as few characters as possible with '*', and then backtracking if necessary.

Algorithm:

  1. Initialize two pointers, i for the text and j for the pattern.
  2. Initialize variables to keep track of the last '*' position in the pattern and the corresponding position in the text.
  3. While i < length of text:
  4. a. If j < length of pattern and (pattern[j] is &apos;?&apos; or pattern[j] matches text[i]), advance both pointers.
  5. b. If j < length of pattern and pattern[j] is &apos;*&apos;, record the position of &apos;*&apos; and the current position in the text, then advance only the pattern pointer.
  6. c. If we can&apos;t match the current characters but we&apos;ve seen a &apos;*&apos; before, backtrack to the position after the last &apos;*&apos; in the pattern and advance the text pointer.
  7. d. If none of the above conditions are met, return false.
  8. Skip any remaining &apos;*&apos; characters in the pattern.
  9. Return true if we&apos;ve reached the end of the pattern, false otherwise.

Time Complexity:

O(m × n)

Where m is the length of the text and n is the length of the pattern. In the worst case, we might need to backtrack for each character in the text, leading to O(m × n) time complexity.

Space Complexity:

O(1)

We only use a constant amount of extra space regardless of the input size.

Approach 3: 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.

This approach is similar to the dynamic programming approach but uses recursion instead of iteration.

Algorithm:

  1. Create a recursive function that takes the current positions in the text and pattern.
  2. Use memoization to store results of subproblems to avoid redundant calculations.
  3. Base case: If we&apos;ve reached the end of the pattern, return true if we&apos;ve also reached the end of the text.
  4. If the current pattern character is &apos;?&apos;, it matches any single character, so move to the next characters in both strings.
  5. If the current pattern character is &apos;*&apos;, we have two choices:
  6. a. Skip the &apos;*&apos; (match zero characters).
  7. b. Match the current text character with the &apos;*&apos; and continue matching.
  8. If the current characters match, move to the next characters in both strings.
  9. 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.

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
function isMatch(text, pattern):
m = length of text
n = length of pattern
// Create a DP table
dp = 2D array of size (m+1) × (n+1) initialized with false
// Empty pattern matches empty text
dp[0][0] = true
// Handle patterns starting with '*'
for j from 1 to n:
if pattern[j-1] == '*':
dp[0][j] = dp[0][j-1]
// Fill the DP table
for i from 1 to m:
for j from 1 to n:
if pattern[j-1] == '?':
// '?' matches any single character
dp[i][j] = dp[i-1][j-1]
else if pattern[j-1] == '*':
// '*' can match zero or more characters
dp[i][j] = dp[i][j-1] || dp[i-1][j]
else:
// Regular character must match
dp[i][j] = (text[i-1] == pattern[j-1]) && dp[i-1][j-1]
return dp[m][n]
ProblemSolutionCode
101 Logo
onenoughtone