InterviewPrepKit

Home / Coding / 2-D Dynamic Programming

Regular Expression Matching

hard Original ↗
Solving tips
  • Set up prefix-vs-prefix DP: dp[i][j] = does s[:i] match p[:j], and require the WHOLE string to match (answer is dp[m][n]).
  • The '*' case is the crux: dp[i][j] = dp[i][j-2] (zero copies of the preceding element) OR (preceding element matches s[i-1] AND dp[i-1][j]) (one more copy) — the repeated char is p[j-2], not p[j-1].
  • '*' means zero-or-more of the SINGLE preceding element, not 'any sequence'; a* matches only a's.
  • Seed the empty-string first row so patterns like a*b* match '' via dp[0][j]=dp[0][j-2]. Target O(m*n) time, O(n) space.

Problem

Given an input string s and a pattern p, decide whether p matches the entire string s. The pattern supports two special characters:

  • . matches any single character.
  • * matches zero or more of the character immediately preceding it.

A * always follows a valid character or .; it never appears first. The match must cover all of s, not just a prefix.

Examples

  • s = "aa", p = "a"False"a" matches only one character, not the whole "aa".
  • s = "aa", p = "a*"Truea* expands to two a’s.
  • s = "ab", p = ".*"True.* is “zero or more of any character”, covering "ab".

Constraints

  • 1 <= len(s) <= 20, 1 <= len(p) <= 30 (roughly)
  • s is lowercase letters; p is lowercase letters plus . and *.
  • Every * has a valid preceding element.
  • Small bounds, but the branching from * (match zero vs. match one-more) makes an O(len(s)·len(p)) table the clean solution.

Think about it first

Hint 1 Match prefixes: ask whether the first `i` characters of `s` match the first `j` characters of `p`. The hard case is when `p[j-1]` is a `*`, because `*` (with its preceding char) can consume zero, one, or many characters of `s`.
Hint 2 When `p[j-1]` is not `*`: the last characters must line up — `s[i-1]` equals `p[j-1]` or `p[j-1]` is `.`, and the shorter prefixes must already match. When `p[j-1]` is `*`: either use it as **zero** copies (drop the `char*` pair, i.e. look at `p[:j-2]`), or, if `p[j-2]` matches `s[i-1]`, use **one more** copy (keep the pattern, drop one char of `s`).
Hint 3 `dp[i][j]` = does `s[:i]` match `p[:j]`. For `p[j-1] == '*'`: `dp[i][j] = dp[i][j-2] or (matches(s[i-1], p[j-2]) and dp[i-1][j])`. Otherwise `dp[i][j] = matches(s[i-1], p[j-1]) and dp[i-1][j-1]`. Seed `dp[0][0] = True` and handle empty-string-vs-`a*b*` patterns in the first row.
Your workspace Not runnable by design — this is your interview scratchpad. Saved on this device.