Wildcard Matching — Solution in Python, Java & C++
Wildcard Matching solution explained: step-by-step approach, complexity and code in Python, Java, C++ and JavaScript. A hard problem.
Wildcard Matching is a hard coding problem on strings, dynamic programming and greedy, asked in Amazon, Google, Meta and Twitter interviews.
- Difficulty: Hard
- Topics: Strings, Dynamic Programming, Greedy
- Asked at: Amazon, Google, Meta, Twitter, Two Sigma
- Time limit: 2 s
- Memory limit: 256 MB
- Languages: JavaScript, TypeScript, Python, Java, C++, C, C#, Go, Kotlin, Swift, Rust, PHP and Ruby
Problem statement
Implement wildcard pattern matching over the whole string:
?matches any single character.*matches any sequence of characters, including the empty sequence.
Return true if p matches all of s.
Example 1
Input: s = "aa", p = "a"
Output: false
Explanation: The pattern must cover the entire string.
Example 2
Input: s = "aa", p = "*"
Output: true
Example 3
Input: s = "cb", p = "?a"
Output: false
Explanation: ? matches c, but a does not match b.
Constraints
0 <= s.length, p.length <= 2000s consists of lowercase English letters.p consists of lowercase English letters, '?' and '*'.
Hints for Wildcard Matching
Hint 1
dp[i][j]= doesp[0..j)matchs[0..i)?Hint 2
A
*either consumes one more character ofs(dp[i-1][j]) or matches nothing (dp[i][j-1]).Hint 3
The first row needs care: a pattern of only stars matches the empty string.
How to solve Wildcard Matching
A two-dimensional boolean table over prefixes. The only branching character is *, which contributes exactly two transitions.
Approach
- Set
dp[0][0] = true; the empty pattern matches the empty string. - Fill row 0:
dp[0][j] = dp[0][j-1]whenp[j-1]is*, otherwise false. - For each cell: if the pattern character is
*,dp[i][j] = dp[i-1][j] || dp[i][j-1]. - If it is
?or equalss[i-1],dp[i][j] = dp[i-1][j-1]. - Otherwise leave it false. Return
dp[n][m].
Why it works
dp[i-1][j] means the star has already absorbed some characters and takes one more; dp[i][j-1] means the star absorbs nothing. Every other pattern character consumes exactly one string character, so it steps diagonally.
Complexity
- Time —
O(n · m) - Space —
O(n · m), reducible to O(m)
Pitfalls
- Forgetting the row-0 star handling rejects
s = ""withp = "***". - Treating
*like the regex*(which applies to the preceding character) is a different problem entirely — here it stands alone. - A greedy two-pointer solution with backtracking also works and is O(1) space, but is much easier to get wrong.
Reference solution
Python
def isMatch(s: str, p: str) -> bool:
n, m = len(s), len(p)
dp = [[False] * (m + 1) for _ in range(n + 1)]
dp[0][0] = True
for j in range(1, m + 1):
if p[j - 1] == '*':
dp[0][j] = dp[0][j - 1]
for i in range(1, n + 1):
for j in range(1, m + 1):
if p[j - 1] == '*':
dp[i][j] = dp[i - 1][j] or dp[i][j - 1]
elif p[j - 1] == '?' or p[j - 1] == s[i - 1]:
dp[i][j] = dp[i - 1][j - 1]
return dp[n][m]Java
public static boolean isMatch(String s, String p) {
int n = s.length(), m = p.length();
boolean[][] dp = new boolean[n + 1][m + 1];
dp[0][0] = true;
for (int j = 1; j <= m; j++) {
if (p.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 pc = p.charAt(j - 1);
if (pc == '*') dp[i][j] = dp[i - 1][j] || dp[i][j - 1];
else if (pc == '?' || pc == s.charAt(i - 1)) dp[i][j] = dp[i - 1][j - 1];
}
}
return dp[n][m];
}C++
bool isMatch(string s, string p) {
int n = (int) s.size(), m = (int) p.size();
vector<vector<char>> dp(n + 1, vector<char>(m + 1, 0));
dp[0][0] = 1;
for (int j = 1; j <= m; j++) {
if (p[j - 1] == '*') dp[0][j] = dp[0][j - 1];
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
char pc = p[j - 1];
if (pc == '*') dp[i][j] = (dp[i - 1][j] || dp[i][j - 1]) ? 1 : 0;
else if (pc == '?' || pc == s[i - 1]) dp[i][j] = dp[i - 1][j - 1];
}
}
return dp[n][m] != 0;
}JavaScript
var isMatch = function(s, p) {
const n = s.length, m = p.length;
const dp = [];
for (let i = 0; i <= n; i++) dp.push(new Array(m + 1).fill(false));
dp[0][0] = true;
for (let j = 1; j <= m; j++) {
if (p.charAt(j - 1) === "*") dp[0][j] = dp[0][j - 1];
}
for (let i = 1; i <= n; i++) {
for (let j = 1; j <= m; j++) {
const pc = p.charAt(j - 1);
if (pc === "*") dp[i][j] = dp[i - 1][j] || dp[i][j - 1];
else if (pc === "?" || pc === s.charAt(i - 1)) dp[i][j] = dp[i - 1][j - 1];
}
}
return dp[n][m];
};Also on the editorial tab: C, C#, Go, Kotlin, PHP, Ruby, Rust, Swift, TypeScript.
All 515 strings problems · the whole catalogue
Learn the technique: Strings · Dynamic Programming