StringProblem 39 of 43
String matching where one string contains wildcard characters
Problem Statement
Given a text string and a wildcard pattern, implement wildcard pattern matching with support for ? and *.
?matches any single character*matches any sequence of characters (including empty sequence)
The matching should cover the entire text string (not partial).
Example:
- Input: text = "aab", pattern = "cab"
- Output: false
Example 2:
- Input: text = "adceb", pattern = "a*c?b"
- Output: true (a*c matches "adc", ? matches "e", b matches "b")
Approach 1: Brute Force (Recursion)
Intuition
For each character in the pattern:
- If it's a regular character, it must match the text character
- If it's
?, it matches any single character - If it's
*, try matching with 0, 1, 2, ... characters from text
Algorithm
- Base case: Both strings exhausted → true
- Base case: Pattern exhausted but text remains → false
- Base case: Text exhausted but pattern has only
*→ check remaining pattern - If current pattern char is
*, try matching:- 0 characters (move pattern pointer)
- 1+ characters (move text pointer)
- If
?or matching char, advance both pointers
java
public class Solution {
public boolean isMatch(String text, String pattern) {
return match(text, pattern, 0, 0);
}
private boolean match(String text, String pattern, int t, int p) {
if (p == pattern.length()) {
return t == text.length();
}
if (t == text.length()) {
for (int i = p; i < pattern.length(); i++) {
if (pattern.charAt(i) != '*') return false;
}
return true;
}
char pChar = pattern.charAt(p);
if (pChar == '*') {
return match(text, pattern, t, p + 1) ||
match(text, pattern, t + 1, p);
}
if (pChar == '?' || pChar == text.charAt(t)) {
return match(text, pattern, t + 1, p + 1);
}
return false;
}
}Complexity Analysis
- Time Complexity: O(2^(n+m)) - Exponential due to
*branching - Space Complexity: O(n + m) - Recursion stack
Approach 2: Memoization (Top-Down DP)
Intuition
The recursive solution has overlapping subproblems. Cache results to avoid recomputation.
java
import java.util.*;
public class Solution {
private int[][] memo;
public boolean isMatch(String text, String pattern) {
int n = text.length(), m = pattern.length();
memo = new int[n + 1][m + 1];
for (int[] row : memo) Arrays.fill(row, -1);
return match(text, pattern, 0, 0);
}
private boolean match(String text, String pattern, int t, int p) {
if (p == pattern.length()) {
return t == text.length();
}
if (t == text.length()) {
for (int i = p; i < pattern.length(); i++) {
if (pattern.charAt(i) != '*') return false;
}
return true;
}
if (memo[t][p] != -1) {
return memo[t][p] == 1;
}
boolean result;
char pChar = pattern.charAt(p);
if (pChar == '*') {
result = match(text, pattern, t, p + 1) ||
match(text, pattern, t + 1, p);
} else if (pChar == '?' || pChar == text.charAt(t)) {
result = match(text, pattern, t + 1, p + 1);
} else {
result = false;
}
memo[t][p] = result ? 1 : 0;
return result;
}
}Complexity Analysis
- Time Complexity: O(n * m)
- Space Complexity: O(n * m)
Approach 3: Tabulation (Bottom-Up DP) - Optimal
Intuition
Build solution bottom-up. dp[i][j] = true if text[0..i-1] matches pattern[0..j-1].
Algorithm
- Initialize
dp[0][0] = true(empty matches empty) - Handle patterns starting with
*(can match empty text) - Fill DP table based on recurrence relations
- Return
dp[n][m]
java
public class Solution {
public boolean isMatch(String text, String pattern) {
int n = text.length(), m = pattern.length();
boolean[][] dp = new boolean[n + 1][m + 1];
dp[0][0] = true;
for (int j = 1; j <= m; j++) {
if (pattern.charAt(j - 1) == '*') {
dp[0][j] = dp[0][j - 1];
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
char pChar = pattern.charAt(j - 1);
char tChar = text.charAt(i - 1);
if (pChar == '*') {
dp[i][j] = dp[i][j - 1] || dp[i - 1][j];
} else if (pChar == '?' || pChar == tChar) {
dp[i][j] = dp[i - 1][j - 1];
}
}
}
return dp[n][m];
}
}Dry Run Example
text = "adceb", pattern = "a*c?b"
DP Table:
"" a * c ? b
"" T F F F F F
a F T T F F F
d F F T F F F
c F F T T F F
e F F T F T F
b F F T F F T
Return dp[5][5] = True
Complexity Analysis
- Time Complexity: O(n * m)
- Space Complexity: O(n * m), can be optimized to O(m)
Approach 4: Space Optimized DP
Key Takeaways
- Two wildcards:
?matches exactly one,*matches zero or more - DP is optimal - avoid exponential recursion
- Space optimization possible from O(n*m) to O(m)
- The
*case: either match empty (move pattern) or match one char (move text) - This is LeetCode 44 - a classic DP problem
- Related: Regular Expression Matching (with
.and*)