From e1ca0500ad13835268ab702c7fbd81311c656627 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 4 Oct 2026 06:10:03 +0000 Subject: [PATCH] feat: add solution for 0678. Valid Parenthesis String --- .../0678-valid-parenthesis-string/analysis.md | 68 ++++++++++++++++++ .../0678-valid-parenthesis-string/problem.md | 69 +++++++++++++++++++ .../solution_daily_20261004.go | 31 +++++++++ .../solution_daily_20261004_test.go | 34 +++++++++ 4 files changed, 202 insertions(+) create mode 100644 problems/0678-valid-parenthesis-string/analysis.md create mode 100644 problems/0678-valid-parenthesis-string/problem.md create mode 100644 problems/0678-valid-parenthesis-string/solution_daily_20261004.go create mode 100644 problems/0678-valid-parenthesis-string/solution_daily_20261004_test.go diff --git a/problems/0678-valid-parenthesis-string/analysis.md b/problems/0678-valid-parenthesis-string/analysis.md new file mode 100644 index 0000000..b14d3fb --- /dev/null +++ b/problems/0678-valid-parenthesis-string/analysis.md @@ -0,0 +1,68 @@ +# 0678. Valid Parenthesis String + +[LeetCode Link](https://leetcode.com/problems/valid-parenthesis-string/) + +Difficulty: Medium +Topics: String, Dynamic Programming, Stack, Greedy, Bracket Sequences +Acceptance Rate: 41.1% + +## Hints + +### Hint 1 + +Without the `'*'`, this is the classic "balanced parentheses" check: scan left to right and track how many `'('` are still open. The string is valid if that count never drops below zero and ends at zero. How does the `'*'` change what that single counter can be? + +### Hint 2 + +Each `'*'` has three choices, so trying every combination is exponential. Instead of tracking *one* open count, think about tracking the *set* of open counts that are reachable after each character. Is that set always a contiguous range? + +### Hint 3 + +Keep two numbers: `lo`, the minimum possible number of open `'('`, and `hi`, the maximum possible. On `'('` both go up; on `')'` both go down; on `'*'` `lo` goes down (treat it as `')'`) and `hi` goes up (treat it as `'('`). If `hi` ever drops below 0, there are too many `')'` — fail. Clamp `lo` at 0, since a negative open count is never a valid state. At the end, the string is valid exactly when `lo == 0`. + +## Approach + +**Greedy range tracking.** + +Think of the open-parenthesis balance as a value that must stay `>= 0` at every prefix and end at exactly `0`. Because `'*'` can be `'('`, `')'`, or empty, after any prefix the balance could be one of several values. The key observation is that these reachable values always form a contiguous range `[lo, hi]` (each `'*'` adds -1, 0, or +1, which fills gaps), so we only need to store its two endpoints. + +Scan the string: + +- `'('`: `lo++`, `hi++` +- `')'`: `lo--`, `hi--` +- `'*'`: `lo--` (use it as `')'`), `hi++` (use it as `'('`) + +After each step: + +1. If `hi < 0`, even treating every `'*'` as `'('` cannot cover the `')'` seen so far → return `false`. +2. If `lo < 0`, set `lo = 0`. A negative balance is an invalid state, so we discard it; this corresponds to choosing "empty" instead of `')'` for some earlier `'*'`. + +At the end, return `lo == 0`: zero must be inside the reachable range (`hi >= 0` is already guaranteed). + +**Example:** `s = "(*))"` + +| char | lo | hi | +|------|----|----| +| `(` | 1 | 1 | +| `*` | 0 | 2 | +| `)` | 0 (clamped from -1) | 1 | +| `)` | 0 (clamped from -1) | 0 | + +`lo == 0` → `true`. (Here the `'*'` acts as `'('`.) + +**Alternatives worth knowing:** +- *Two stacks* (indices of `'('` and of `'*'`): match each `')'` with a `'('` first, else a `'*'`; then pair leftover `'('` with `'*'` that appear *after* them. O(n) time, O(n) space. +- *Interval DP* `dp[i][j]` = whether `s[i..j]` is valid: O(n³) time — fine for n ≤ 100 but much slower. + +## Complexity Analysis + +Time Complexity: O(n) — a single pass over the string. +Space Complexity: O(1) — only two integer counters. + +## Edge Cases + +- **Leading `')'`** (e.g. `")("`): `hi` goes negative immediately; must fail fast rather than wait for a later `'('` to "fix" it. +- **Only stars** (e.g. `"***"`): every star can be empty, so it's valid; `lo` stays clamped at 0. +- **Unmatched `'('` with a star before it** (e.g. `"*("`): a `'*'` can't close a `'('` that comes *after* it; `lo` ends at 1 → `false`. This is the order constraint that naive counting misses. +- **Single character**: `"("` and `")"` are invalid, `"*"` is valid. +- **Stars needed as closers** (e.g. `"((*"`): only one star for two opens → `lo` ends at 1 → `false`. diff --git a/problems/0678-valid-parenthesis-string/problem.md b/problems/0678-valid-parenthesis-string/problem.md new file mode 100644 index 0000000..5f77653 --- /dev/null +++ b/problems/0678-valid-parenthesis-string/problem.md @@ -0,0 +1,69 @@ +--- +number: "0678" +frontend_id: "678" +title: "Valid Parenthesis String" +slug: "valid-parenthesis-string" +difficulty: "Medium" +topics: + - "String" + - "Dynamic Programming" + - "Stack" + - "Greedy" + - "Bracket Sequences" +acceptance_rate: 4113.3 +is_premium: false +created_at: "2026-10-04T06:09:07.468526+00:00" +fetched_at: "2026-10-04T06:09:07.468526+00:00" +link: "https://leetcode.com/problems/valid-parenthesis-string/" +date: "2026-10-04" +--- + +# 0678. Valid Parenthesis String + +Given a string `s` containing only three types of characters: `'('`, `')'` and `'*'`, return `true` _if_ `s` _is**valid**_. + +The following rules define a **valid** string: + + * Any left parenthesis `'('` must have a corresponding right parenthesis `')'`. + * Any right parenthesis `')'` must have a corresponding left parenthesis `'('`. + * Left parenthesis `'('` must go before the corresponding right parenthesis `')'`. + * `'*'` could be treated as a single right parenthesis `')'` or a single left parenthesis `'('` or an empty string `""`. + + + + + +**Example 1:** + + + **Input:** s = "()" + **Output:** true + + +**Example 2:** + + + **Input:** s = "(*)" + **Output:** true + + +**Example 3:** + + + **Input:** s = "(*))" + **Output:** true + + +**Example 4:** + + + **Input:** s = "(" + **Output:** false + + + + +**Constraints:** + + * `1 <= s.length <= 100` + * `s[i]` is `'('`, `')'` or `'*'`. diff --git a/problems/0678-valid-parenthesis-string/solution_daily_20261004.go b/problems/0678-valid-parenthesis-string/solution_daily_20261004.go new file mode 100644 index 0000000..20645e0 --- /dev/null +++ b/problems/0678-valid-parenthesis-string/solution_daily_20261004.go @@ -0,0 +1,31 @@ +package main + +// Approach: greedy range tracking. +// Track the minimum (lo) and maximum (hi) number of unmatched '(' possible +// after each prefix. '*' widens the range in both directions. If hi < 0 the +// string has too many ')' and can never be valid. lo is clamped at 0 since a +// negative balance is never a valid state. The string is valid iff lo == 0 at +// the end. O(n) time, O(1) space. +func checkValidString(s string) bool { + lo, hi := 0, 0 + for i := 0; i < len(s); i++ { + switch s[i] { + case '(': + lo++ + hi++ + case ')': + lo-- + hi-- + default: // '*' + lo-- + hi++ + } + if hi < 0 { + return false + } + if lo < 0 { + lo = 0 + } + } + return lo == 0 +} diff --git a/problems/0678-valid-parenthesis-string/solution_daily_20261004_test.go b/problems/0678-valid-parenthesis-string/solution_daily_20261004_test.go new file mode 100644 index 0000000..50ac4f4 --- /dev/null +++ b/problems/0678-valid-parenthesis-string/solution_daily_20261004_test.go @@ -0,0 +1,34 @@ +package main + +import "testing" + +func TestSolution(t *testing.T) { + tests := []struct { + name string + s string + expected bool + }{ + {"example 1: simple pair", "()", true}, + {"example 2: star as empty", "(*)", true}, + {"example 3: star as open", "(*))", true}, + {"example 4: single open", "(", false}, + {"edge case: single close", ")", false}, + {"edge case: single star", "*", true}, + {"edge case: only stars", "***", true}, + {"edge case: close before open", ")(", false}, + {"edge case: star before open cannot close it", "*(", false}, + {"edge case: not enough stars to close", "((*", false}, + {"edge case: stars close multiple opens", "((**", true}, + {"edge case: star as close at start invalid", "*)))", false}, + {"edge case: long mixed valid", "(*()*)(*)*", true}, + {"edge case: leetcode tricky", "(((((*(()((((*((**(((()()*)()()()*((((**)())*)*)))))))(())(()))())((*()()(((()((()*(())*(()**)()(())", false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := checkValidString(tt.s) + if result != tt.expected { + t.Errorf("checkValidString(%q) = %v, want %v", tt.s, result, tt.expected) + } + }) + } +}