diff --git a/problems/0032-longest-valid-parentheses/analysis.md b/problems/0032-longest-valid-parentheses/analysis.md new file mode 100644 index 0000000..e20e697 --- /dev/null +++ b/problems/0032-longest-valid-parentheses/analysis.md @@ -0,0 +1,64 @@ +# 0032. Longest Valid Parentheses + +[LeetCode Link](https://leetcode.com/problems/longest-valid-parentheses/) + +Difficulty: Hard +Topics: String, Dynamic Programming, Stack, Bracket Sequences +Acceptance Rate: 40.2% + +## Hints + +### Hint 1 + +Checking whether a whole string is balanced is a classic **stack** problem. Here you need the longest balanced *substring*, so think about how a stack could also tell you where each valid stretch *starts*. + +### Hint 2 + +Instead of pushing characters onto the stack, push **indices**. When a `)` matches a `(`, the length of the valid run ending here can be computed from an index that is still on the stack. + +### Hint 3 + +Keep a "boundary" index at the bottom of the stack: the position just before the current valid run could begin. Start with `-1` as the boundary. On `(`, push its index. On `)`, pop. If the stack becomes empty, this `)` is unmatched, so push its index as the new boundary. Otherwise, the valid run ending at `i` has length `i - stack.top()`. + +## Approach + +We scan the string once, keeping a stack of indices. + +1. Push `-1` onto the stack. It acts as a sentinel boundary: "the last position that can't be part of a valid substring". +2. For each index `i`: + - If `s[i] == '('`, push `i`. It might be matched later. + - If `s[i] == ')'`, pop the top. + - If the stack is now **empty**, that `)` had nothing to match. It becomes the new boundary, so push `i`. + - Otherwise, the top of the stack is the index just before the current valid substring, so update `best = max(best, i - top)`. + +**Why it works:** At any time, the stack holds the indices of unmatched `(` characters, sitting on top of the index of the most recent unmatched `)` (or `-1`). Everything between the top of the stack and `i` has been matched, so it forms a valid substring. + +**Example:** `s = ")()())"` + +| i | char | action | stack | best | +|---|------|--------|-------|------| +| - | - | init | [-1] | 0 | +| 0 | ) | pop → empty, push 0 | [0] | 0 | +| 1 | ( | push 1 | [0, 1] | 0 | +| 2 | ) | pop → top 0, len 2 | [0] | 2 | +| 3 | ( | push 3 | [0, 3] | 2 | +| 4 | ) | pop → top 0, len 4 | [0] | 4 | +| 5 | ) | pop → empty, push 5 | [5] | 4 | + +Answer: `4`. + +**Alternative (O(1) space):** Scan left to right counting `open` and `close`. When they're equal, record `2 * close`. When `close > open`, reset both to zero. That misses cases like `"(()"`, where there are always more opens than closes, so do a second scan right to left with the reset rule flipped (`open > close`). There is also an O(n) DP where `dp[i]` is the length of the longest valid substring ending at `i`. + +## Complexity Analysis + +Time Complexity: O(n), one pass over the string, with each index pushed and popped at most once. +Space Complexity: O(n) for the stack in the worst case (e.g. `"(((((("`). The two-counter variant uses O(1). + +## Edge Cases + +- **Empty string**: there's nothing to match, so the answer is 0. The loop simply doesn't run. +- **Only `(` or only `)`**: nothing ever matches, so the answer is 0. The sentinel and boundary logic must not produce false lengths. +- **Leading unmatched `)`**: for example `")()"`. The empty-stack reset makes the `)` the new boundary. +- **Unmatched `(` in the middle or at the start**: for example `"(()"`. The leftover `(` stays on the stack and serves as the boundary for later matches. +- **Nested and adjacent groups combined**: for example `"()(())"`. These must be counted as one run of length 6, which the boundary index handles naturally. +- **Whole string valid**: the sentinel `-1` gives length `n - 1 - (-1) = n`. diff --git a/problems/0032-longest-valid-parentheses/problem.md b/problems/0032-longest-valid-parentheses/problem.md new file mode 100644 index 0000000..44949be --- /dev/null +++ b/problems/0032-longest-valid-parentheses/problem.md @@ -0,0 +1,54 @@ +--- +number: "0032" +frontend_id: "32" +title: "Longest Valid Parentheses" +slug: "longest-valid-parentheses" +difficulty: "Hard" +topics: + - "String" + - "Dynamic Programming" + - "Stack" + - "Bracket Sequences" +acceptance_rate: 4015.4 +is_premium: false +created_at: "2026-10-03T05:33:09.859329+00:00" +fetched_at: "2026-10-03T05:33:09.859329+00:00" +link: "https://leetcode.com/problems/longest-valid-parentheses/" +date: "2026-10-03" +--- + +# 0032. Longest Valid Parentheses + +Given a string containing just the characters `'('` and `')'`, return _the length of the longest valid (well-formed) parentheses_ _substring_. + + + +**Example 1:** + + + **Input:** s = "(()" + **Output:** 2 + **Explanation:** The longest valid parentheses substring is "()". + + +**Example 2:** + + + **Input:** s = ")()())" + **Output:** 4 + **Explanation:** The longest valid parentheses substring is "()()". + + +**Example 3:** + + + **Input:** s = "" + **Output:** 0 + + + + +**Constraints:** + + * `0 <= s.length <= 3 * 104` + * `s[i]` is `'('`, or `')'`. diff --git a/problems/0032-longest-valid-parentheses/solution_daily_20261003.go b/problems/0032-longest-valid-parentheses/solution_daily_20261003.go new file mode 100644 index 0000000..9c51e79 --- /dev/null +++ b/problems/0032-longest-valid-parentheses/solution_daily_20261003.go @@ -0,0 +1,25 @@ +package main + +// Approach: stack of indices with a boundary sentinel. +// The bottom of the stack always holds the index just before the current +// candidate valid substring (initially -1). Push indices of '('. On ')', pop; +// if the stack becomes empty the ')' is unmatched and becomes the new boundary, +// otherwise the valid run ending at i has length i - top. +// Time: O(n), Space: O(n). +func longestValidParentheses(s string) int { + stack := []int{-1} + best := 0 + for i := 0; i < len(s); i++ { + if s[i] == '(' { + stack = append(stack, i) + continue + } + stack = stack[:len(stack)-1] + if len(stack) == 0 { + stack = append(stack, i) + } else if l := i - stack[len(stack)-1]; l > best { + best = l + } + } + return best +} diff --git a/problems/0032-longest-valid-parentheses/solution_daily_20261003_test.go b/problems/0032-longest-valid-parentheses/solution_daily_20261003_test.go new file mode 100644 index 0000000..408cdfd --- /dev/null +++ b/problems/0032-longest-valid-parentheses/solution_daily_20261003_test.go @@ -0,0 +1,35 @@ +package main + +import "testing" + +func TestLongestValidParentheses(t *testing.T) { + tests := []struct { + name string + s string + expected int + }{ + {"example 1: unmatched leading open", "(()", 2}, + {"example 2: unmatched on both ends", ")()())", 4}, + {"example 3: empty string", "", 0}, + {"edge case: single open", "(", 0}, + {"edge case: single close", ")", 0}, + {"edge case: all opens", "((((", 0}, + {"edge case: all closes", "))))", 0}, + {"edge case: wrong order", ")(", 0}, + {"edge case: entire string valid nested and adjacent", "()(())", 6}, + {"edge case: deeply nested", "((()))", 6}, + {"edge case: break splits runs", "()(()", 2}, + {"edge case: longer run after break", "())(())()", 6}, + {"edge case: unmatched open in middle", "(()(((()", 2}, + {"edge case: valid run at end", "))((()))", 6}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := longestValidParentheses(tt.s) + if result != tt.expected { + t.Errorf("longestValidParentheses(%q) = %d, want %d", tt.s, result, tt.expected) + } + }) + } +}