Below is a clean Markdown (README.md) file that fully explains the entire regex matcher code, including:
✅ Feature overview ✅ Recursive + memoized explanation ✅ Iterative DP explanation ✅ Benchmarks ✅ Complexity ✅ Examples ✅ Visual diagrams
# Regular Expression Matcher in Go
Supports `.` and `*` Operators
This project implements a simplified regular expression engine in Go.
It supports:
- `.` — matches **any one character**
- `*` — matches **zero or more** of the preceding character
- Full backtracking prevention using **DP (Memoized Recursion + Iterative DP)**
This is equivalent to the behavior required in **LeetCode #10 – Regular Expression Matching**.
---
# 🚀 Features
✔ Complete recursive solution with memoization
✔ Iterative bottom-up DP version
✔ Full test suite
✔ Go benchmarks
✔ Handles complex expressions like:
"aab" vs "cab" → true "aaa" vs "abaca" → true "mississippi" vs "misisp." → false
---
# 📌 1. Recursive + Memoized Version (Top-Down DP)
The recursive version uses a function:
dp(i, j)
which checks:
> Does `s[i:]` match `p[j:]`?
### Why memoization?
Regex backtracking can explode exponentially (e.g., `aaa...` vs `a*a*a*...`).
Memoization turns it into **O(m × n)**.
### Rules
#### 1. If pattern is finished
match only if string is also finished
#### 2. Check first character match
firstMatch = s[i] == p[j] || p[j] == '.'
#### 3. If next pattern char is `*`
Two choices:
-
Skip "x*": dp(i, j+2)
-
Use "*" at least once: firstMatch && dp(i+1, j)
#### 4. If no `*`
Must match one character:
firstMatch && dp(i+1, j+1)
The memo map stores every `(i, j)` result to avoid recomputation.
---
# 📌 2. Iterative Bottom-Up DP Version
We build a DP matrix:
dp[i][j] = does s[:i] match p[:j] ?
Matrix size → `(len(s)+1) × (len(p)+1)`
### Base Case
dp[0][0] = true
### Patterns like `a*`, `a*b*`, etc.
for j in pattern: if p[j] == '*' and dp[0][j-1] == true: dp[0][j+1] = true
### DP Transitions
#### If characters match:
dp[i+1][j+1] = dp[i][j]
#### If encountering `*`:
- Zero occurrences:
dp[i+1][j+1] = dp[i+1][j-1]
- One/more occurrences:
if p[j-1] matches s[i]: dp[i+1][j+1] = dp[i][j+1]
---
# 📌 3. Test Cases
Examples used:
| String | Pattern | Expected |
|--------|---------|----------|
| `aa` | `a` | false |
| `aa` | `a*` | true |
| `ab` | `.*` | true |
| `aab` | `c*a*b` | true |
| `mississippi` | `mis*is*p*.` | false |
| `aaa` | `ab*a*c*a` | true |
---
# 📌 4. Benchmarks
Includes:
BenchmarkRecursive BenchmarkIterative BenchmarkLargeInput
Run:
go test -bench=.
Measures performance of both solutions.
---
# 📌 5. Complexity Analysis
Both versions run in:
### ⏱ Time: **O(m × n)**
Every `(i, j)` state computed once.
### 💾 Space: **O(m × n)**
Due to DP table or memo caching.
---
# 📌 6. Example Matching Breakdown
### Example
s = "aab" p = "cab"
Pattern expands to:
c* → matches "" a* → matches "aa" b → matches "b"
Thus:
aab matches cab → true
---
# 📌 7. Visualization of DP Matrix
For string `"ab"` and pattern `".*"`:
| i\j | 0 | . | * |
|-----|---|---|---|
| 0 | T | F | T |
| a | F | T | T |
| b | F | F | T |
Final cell:
dp[2][2] = true
---
# 📌 8. File Structure
regex.go → full implementations + tests in main() regex_test.go → benchmarks README.md → explanation
---
# 📌 9. How to Run
Run the main program:
go run .
Run tests:
go test
Run benchmarks:
go test -bench=.
This Go project demonstrates a complete regex engine supporting . and * using two approaches:
- Recursive memoized DP (clean and elegant)
- Iterative bottom-up DP (efficient and iterative)
Both approaches guarantee O(m × n) performance with full correctness.