Wildcard Matching asks whether a pattern with ? and * wildcards matches an entire string. It is a classic 2-D dynamic-programming problem: the answer for the whole input is built from the answers for every pair of prefixes.
Problem. Given a string s and a pattern p, return whether p matches the entire string s.
The pattern may contain ? (matches any single character) and * (matches any sequence of characters,
including the empty sequence). Plain characters must match exactly.
Example: s = "ab", p = "*b" → True (the * absorbs "a", then the literal "b" matches).
The slow way first
The tempting approach is recursion: at each character decide what * should eat, branching on every possibility. But a * can match 0, 1, 2, … characters, so naive recursion explores an exponential number of branches and re-solves the same (i, j) prefix pair over and over. For a long string with several stars it times out.
The question to ask: what sub-answers do I keep recomputing? Always the same thing — "does the first i characters of s match the first j characters of p?" There are only (n+1) × (m+1) such questions, so we compute each one once and store it.
The idea: a grid of prefix answers
Let dp[i][j] mean "does p[0..j) match s[0..i)?". Fill the grid row by row. The recurrence depends only on the last pattern character:
- If
p[j-1]is*, it can consume one more character ofs(look updp[i-1][j]) or match the empty string (look updp[i][j-1]). Either path winning meansTrue. - If
p[j-1]is?or equalss[i-1], the last characters line up, so inheritdp[i-1][j-1]. - Otherwise the characters disagree and
dp[i][j]isFalse.
The seed is dp[0][0] = True (empty matches empty). The first row handles a pattern matched against the empty string: a leading run of * stays True, anything else turns False.
Walk through it
Step through the animation for s = "ab", p = "*b". We seed the corner, fill the top row (the leading * keeps it True), then fill each inner cell. At dp[2][2] the literal b matches s's last b, so it copies the diagonal dp[1][1] = True. The bottom-right cell is the final answer.
Pseudocode
dp[0][0] = True # empty pattern matches empty string
for j in 1..m: # pattern vs the empty string
if p[j-1] == "*": dp[0][j] = dp[0][j-1]
else: dp[0][j] = False
for i in 1..n:
for j in 1..m:
if p[j-1] == "*":
dp[i][j] = dp[i-1][j] or dp[i][j-1] # consume OR empty
else if p[j-1] == "?" or p[j-1] == s[i-1]:
dp[i][j] = dp[i-1][j-1] # chars line up
else:
dp[i][j] = False
return dp[n][m]The Python solution
def is_match(s, p):
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]
else:
dp[0][j] = False
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]
else:
dp[i][j] = False
return dp[n][m]dpis an(n+1) × (m+1)table; rowi, columnjanswers the prefix question for lengthsiandj.dp[0][0] = Trueis the seed — an empty pattern matches an empty string.- The first loop fills the top row: only a leading run of
*can match the empty string, so the value carries left-to-right while we see stars and drops toFalseafter. - Line 13 is the
*branch — the two-way OR is the heart of the algorithm: consume one char (dp[i-1][j]) or match empty (dp[i][j-1]). - Line 15 handles
?and an exact character match by inheriting the diagonaldp[i-1][j-1]. dp[n][m]is whether the whole pattern matches the whole string.
Complexity
| Case | Time | Notes |
|---|---|---|
| Naive recursion | O(2^(n+m)) (moderate) | stars branch exponentially |
| DP table (this solution) | O(n * m) (moderate) | each cell filled once |
O(n * m) (moderate)We trade an O(n·m) table for a dramatic speed-up. The table can be shrunk to a single row (O(m) space) since each cell only reads the current and previous row, but the full grid is clearer to reason about.
When this pattern shows up
Whenever a problem matches or aligns two sequences — string-vs-pattern, edit distance, longest common
subsequence — reach for a 2-D dp grid indexed by prefix lengths. The move is always the same: decide
what the last element does, then look up the smaller sub-answers it depends on.
Do not confuse * here with regex. In wildcard matching * stands alone and matches any sequence; it is
not a quantifier attached to the previous character. That is the key difference from the harder
Regular Expression Matching problem.
Practice
Filling dp[i][j] when p[j-1] is '*', which two earlier cells do you OR together, and what does each one mean?
1. What does dp[i][j] represent?
2. When p[j-1] is '*', dp[i][j] is computed as:
3. What is dp[0][0] seeded to and why?
4. What is the time complexity of the DP solution?