We need to check if pattern p fully matches string s. The hard character is '*' because it can match any sequence, including nothing at all. A single '*' could consume zero characters, one character, or the entire remaining string. This branching is the source of the difficulty.
The '?' wildcard matches exactly one character, whatever that character is. Regular characters must match exactly. The open question is: when we see a '*', how many characters from s should it consume?
The number of characters one '*' consumes changes what the rest of the pattern has to match. Trying every possibility by brute force re-solves the same suffix-against-suffix comparison many times, which is the signal for dynamic programming.
0 <= s.length, p.length <= 2000 means an O(n * m) solution is at most about 4 million operations, well within limits, so we do not need anything faster than quadratic.s contains only lowercase English letters, so wildcards appear only in p. We never have to interpret a wildcard on the string side.s against a pattern of only stars ("***") must return true, since each star can match the empty sequence.Define the problem recursively: does the suffix s[i:] match the suffix p[j:]? We compare characters from left to right. At each step, we look at s[i] and p[j]:
p[j] is a regular character, it must equal s[i], and we advance both pointers.p[j] is '?', it matches any single character, so we advance both pointers.p[j] is '*', we branch. The '*' can match zero characters (skip it, advance j) or match one character (consume s[i], keep j at the '*' so it can match more).The branch on '*' lets the same (i, j) pair be reached through different paths. Caching the result for each (i, j) pair turns the exponential recursion into one that does work proportional to the number of distinct pairs.
match(i, j) that returns whether s[i:] matches p[j:].i and j are past the end, return true. If only j is past the end but s has characters left, return false. If only i is past the end, return true only if all remaining characters in p are '*'.p[j] is '*', try two branches: match(i, j+1) (star matches nothing) or match(i+1, j) (star matches s[i] and stays).p[j] is '?' or matches s[i], recurse with match(i+1, j+1).(i, j).The recursive version carries function-call overhead and, in some languages, hash-map lookups. The next approach fills the same table iteratively, which removes the recursion stack and the call overhead.
Instead of recursing top-down with memoization, we build the answer bottom-up. Define dp[i][j] as whether the first i characters of s match the first j characters of p. We fill this table from smaller prefixes to larger ones.
The transitions mirror the recursive cases, indexed by prefix length rather than suffix position:
p[j-1] is a letter or '?', then dp[i][j] = dp[i-1][j-1] when the characters match (and false otherwise).p[j-1] is '*', then dp[i][j] = dp[i][j-1] (star matches empty) OR dp[i-1][j] (star matches one more character from s).In the star transition dp[i][j] = dp[i][j-1] || dp[i-1][j], the first term drops the star and matches s[0..i-1] against p[0..j-2]. The second term consumes s[i-1] with the star but reuses column j, not j-1. Reusing j leaves the star in the pattern so it can consume more characters of s on later rows. If the second term used dp[i-1][j-1], the star would match exactly one character, which is the behavior of '?', not '*'.
dp of size (n+1) x (m+1), initialized to false.dp[0][0] = true.dp[0][j] = true if p[0..j-1] is all stars.dp[i][j]:p[j-1] == '*': dp[i][j] = dp[i][j-1] || dp[i-1][j]p[j-1] == '?' || p[j-1] == s[i-1]: dp[i][j] = dp[i-1][j-1]dp[n][m].The DP approach uses O(n * m) space. The next approach drops the table entirely and matches greedily with O(1) extra space, backtracking only at the most recent star.
Instead of DP, we scan s and p together and handle '*' by remembering the last star's position, backtracking to it when a later mismatch occurs.
The procedure: when characters match (or the pattern has '?'), advance both pointers. When we reach a '*', record its pattern position and the current position in s, then advance only the pattern pointer, which tries matching the star against zero characters first.
On a later mismatch, reset the pattern pointer to just past the recorded star and let the star consume one more character of s. We repeat this until we either consume all of s or run out of stars to extend.
We only ever backtrack to the most recent '*'. With two stars *1 then *2, suppose a mismatch happens after *2. Extending *1 would only shift where *2 begins matching, and any extra characters *1 could absorb, *2 can absorb just as well, since both match arbitrary sequences. So extending *2 covers every assignment that extending *1 would. This holds because wildcard '*' matches any sequence regardless of content. It would not hold for regex *, where each star is tied to a specific preceding character.
sIdx = 0 (current position in s), pIdx = 0 (current position in p), starIdx = -1 (position of last '*' in p), matchIdx = 0 (position in s where we started matching after the last '*').sIdx < len(s):p[pIdx] matches s[sIdx] (same character or '?'), advance both pointers.p[pIdx] == '*', record starIdx = pIdx and matchIdx = sIdx, then advance pIdx (try matching star with zero characters).pIdx to starIdx + 1, increment matchIdx (star now matches one more character), and set sIdx = matchIdx.'*' characters in p.pIdx == len(p).