Wildcard Matching
Solve this Problem?), and multi-character wildcards (*) is checked against a target string — the question is whether the pattern can be stretched or squeezed to cover the string entirely, not just a piece of it. Letters and ? are easy: they simply have to line up one-to-one. The * is what makes this genuinely tricky, since it isn't pinned to any particular number of characters, so a naive check has to be willing to try every length it could possibly absorb.
The way through is to stop asking "how many characters does this * eat" up front, and instead ask a simpler yes/no question at every (position in s, position in p) pair: can the pattern up to here match the string up to here? A * then has just two ways to answer yes — either it isn't needed at all here (check without it), or it's already covering the last character of s and the same question just shifts one character earlier in s. Chaining that logic across the whole grid answers the original question without ever guessing a length.
Test Case 1:
Test Case 2:
Test Case 3:
Constraints
- ◆
0 ≤ s.length ≤ 10 - ◆
0 ≤ p.length ≤ 10 - ◆
s consists of lowercase English letters only - ◆
p consists of lowercase English letters, '?', and '*'
Try the Dry Run
Don't just read the solution — watch it execute, one step at a time.
Approach & Solutions
Brute Force — Recursive Without Memoization
BruteAt each position, if the pattern character is '*', it can either vanish — matching zero characters and letting the rest of the pattern take over from the same spot in s — or eat one character of s and stick around to decide again next time. Trying both keeps every possibility on the table. A literal letter or '?' can only step forward together with s, one character at a time, and only if they actually agree. The catch is that the same (position in s, position in p) pair gets asked about repeatedly through completely different chains of '*' decisions, so the work balloons fast.
O(2ⁿ⁺ᵐ)O(n+m)1class Solution {
2 private String s, p;
3 public boolean isMatch(String s, String p) {
4 this.s = s;
5 this.p = p;
6 return solve(0, 0);
7 }
8 private boolean solve(int i, int j) {
9 if (j == p.length()) return i == s.length();
10 boolean firstMatch = i < s.length() && (p.charAt(j) == '?' || p.charAt(j) == s.charAt(i));
11 if (p.charAt(j) == '*') {
12 return solve(i, j + 1) || (i < s.length() && solve(i + 1, j));
13 }
14 return firstMatch && solve(i + 1, j + 1);
15 }
16}Optimal — Bottom-Up DP
OptimalTrack, for every prefix of s and every prefix of p, whether that prefix of p can fully cover that prefix of s. A literal or '?' just inherits the answer from one character back on both sides. A '*' is more generous — it inherits the answer either from leaving it out entirely (one column back, same row) or from having already used it to soak up the current character of s (same column, one row back); either path succeeding is enough. The very first row needs its own pass beforehand, since only a run of pure '*'s can ever match an empty s.
O(n*m)O(n*m)1class Solution {
2 public boolean isMatch(String s, String p) {
3 int n = s.length(), m = p.length();
4 boolean[][] dp = new boolean[n + 1][m + 1];
5 dp[0][0] = true;
6 for (int j = 1; j <= m; j++) {
7 if (p.charAt(j - 1) == '*') dp[0][j] = dp[0][j - 1];
8 }
9 for (int i = 1; i <= n; i++) {
10 for (int j = 1; j <= m; j++) {
11 char pc = p.charAt(j - 1);
12 if (pc == '*') {
13 dp[i][j] = dp[i - 1][j] || dp[i][j - 1];
14 } else if (pc == '?' || pc == s.charAt(i - 1)) {
15 dp[i][j] = dp[i - 1][j - 1];
16 }
17 }
18 }
19 return dp[n][m];
20 }
21}