Regular Expression Matching
Solve this Problem.), and a repeat marker (*) is checked against a target string for a full match, not a partial one. The tricky part is that * doesn't stand alone — it always modifies whatever came right before it, meaning that character (or .) can appear zero times, once, or many times in a row. A naive check has to be willing to try every repeat count a * pair could possibly use before it knows whether the whole match works out.
The way through is the same idea used for matching without repeats: build up an answer to "can this much of the pattern explain this much of the string" one small piece at a time. A * pair offers two independent paths to a yes — skip it completely, or use it once and lean on a smaller version of the same question. Filling in that grid systematically, from the empty prefixes outward, sidesteps the need to ever guess how many repetitions to try.
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 '*' - ◆
It is guaranteed that for each appearance of '*', there is a previous valid character to match
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
BruteLook only at the first characters of what's left of s and p. If the character right after the current pattern position is '*', that pair (character + '*') can either be dropped entirely — matching zero occurrences and moving on in the pattern only — or, if the current pattern character actually matches the current string character, one occurrence can be consumed from s while staying on the same pattern position to consider repeating it again. Without a following '*', the current characters must match outright and both strings shrink together. The problem keeps reducing to a smaller version of itself until either string runs out, but the same (s, p) pair can be reached via different repeat counts, so a lot of the work repeats.
O(2ⁿ⁺ᵐ)O(n+m)1class Solution {
2 public boolean isMatch(String s, String p) {
3 if (p.isEmpty()) return s.isEmpty();
4 boolean firstMatch = !s.isEmpty() && (p.charAt(0) == '.' || p.charAt(0) == s.charAt(0));
5 if (p.length() >= 2 && p.charAt(1) == '*') {
6 return isMatch(s, p.substring(2)) || (firstMatch && isMatch(s.substring(1), p));
7 }
8 return firstMatch && isMatch(s.substring(1), p.substring(1));
9 }
10}Optimal — Bottom-Up DP
OptimalTrack, for every prefix of s and every prefix of p, whether that prefix of p can fully explain that prefix of s. When the current pattern character is followed by '*', that pair contributes in one of two ways — either it's skipped altogether, so the answer copies straight from two columns back on the same row, or, if the character right before the '*' actually matches the current character of s, one repetition is peeled off and the answer copies from the row above on the same column. Without a '*', a plain letter or '.' just needs the previous prefixes to already agree, diagonally. The first row gets special treatment up front, since only patterns built entirely from "x*" pairs 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 - 2];
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 char prev = p.charAt(j - 2);
14 dp[i][j] = dp[i][j - 2] || ((prev == '.' || prev == s.charAt(i - 1)) && dp[i - 1][j]);
15 } else if (pc == '.' || pc == s.charAt(i - 1)) {
16 dp[i][j] = dp[i - 1][j - 1];
17 }
18 }
19 }
20 return dp[n][m];
21 }
22}