Regular Expression Matching Solution
Implement regular expression matching with . (any char) and * (zero or more of preceding element).
Problem Statement
Given string s and pattern p, implement regex matching where . matches any single character and * matches zero or more of the preceding element. The match must cover the entire string.
Recursion — exponential
Process pattern left to right. If the next pattern character has a *, try matching zero occurrences (skip the pair) or one occurrence (if char matches, advance in string but keep pattern). Otherwise, match one character and advance both.
Exhaustively tries all ways to apply * (zero or more matches) and direct matches. If any branch succeeds, the match is valid.
* causes exponential blowup. States are (i,j) — position in s and p. At most m×n unique states. 2D DP stores each once.| Metric | Value |
|---|---|
| Time | Exponential without memo |
| Space | O(m+n) |
2D DP — O(m·n)
dp[i][j] = true if s[0..i-1] matches p[0..j-1]. Handle three cases: direct character match, . wildcard, and * repetition.
. is a universal pin that matches any notch. * is a spring-loaded section that can stretch to match zero or more notches of the preceding pin type. The grid maps every (key position, dial position) pair: "Can the remaining key match the remaining dial?" You fill this grid bottom-right to top-left (or top-left to bottom-right with careful indexing).dp[0][0] = true(empty string matches empty pattern).- First row:
dp[0][j] = trueifp[j-1]=='*'anddp[0][j-2]is true (a* matches ""). - For i=1..m, j=1..n:
- If
p[j-1]is letter or.:dp[i][j] = dp[i-1][j-1]if chars match. - If
p[j-1]=='*':dp[i][j] = dp[i][j-2](zero occurrences) OR (dp[i-1][j]ifs[i-1]matchesp[j-2]— one more occurrence). - Return
dp[m][n].
* handling is the tricky part — it's always a "zero or more" choice. Related: LC 44 (Wildcard Matching — * matches any sequence, slightly different semantics).p[j-1]=='*' has two sub-cases: (1) Use zero of the preceding char → dp[i][j-2]. (2) Use one more of the preceding char → dp[i-1][j] if current string char matches the preceding pattern char. Case (2) is recursive — if we match one more, we stay at j (the * still applies), but advance i.Visual Walkthrough
Trace for s="aab", p="c*a*b".
Code — Java & Python
Complexity Analysis
| Approach | Time | Space | Trade-off |
|---|---|---|---|
| Recursion (no memo) | Exponential | O(m+n) | Simple but slow |
| Memoized recursion | O(m·n) | O(m·n) | Top-down; cleaner logic |
| 2D DP ← optimal | O(m·n) | O(m·n) | Bottom-up; handles all edge cases cleanly |
* means zero or more of the preceding element (regex semantics). In LC 44, * matches any sequence of characters (glob semantics). The DP transitions differ: LC 10's * references p[j-2], while LC 44's * is standalone. Don't mix them up.Edge Cases & Pitfalls
| Case | Input | Why It Matters |
|---|---|---|
| Empty pattern | s="a", p="" | p is empty but s isn't. dp[1][0]=false. |
| Star matches zero | s="a", p="ab*" | b* = "". Pattern becomes "a". Match. |
| Dot-star | s="anything", p=".*" | .* matches any string of any length. |
| Multiple stars | s="aab", p="c*a*b" | c*="" then a*="aa" then b="b". |
| Star at start | Not possible | Constraints guarantee * is always preceded by valid char. |
* case, checking dp[i-1][j-1] instead of dp[i-1][j] for "one more occurrence." When * matches one more character, the * is still active — we stay at column j (not j-1). dp[i-1][j] means "same pattern (including the *), one fewer string char" — allowing another match next iteration.