← DSA
MEDIUM

Valid Parenthesis String

PracticeNeetCode ↗LeetCode ↗

Given a string s made only of '(', ')', and '*' — where each '*' can stand in for '(', ')', or an empty string — return true if some interpretation of the '*'s makes s a valid parentheses string.

Example

Input s = "(*))"
Output true

Key Pointers

Step-by-Step Analysis

  1. Initialize loMin = 0, hiMax = 0.
  2. For each character c: if '(', increment both bounds; if ')', decrement both; if '*', decrement loMin and increment hiMax.
  3. If hiMax < 0 at any point, return False.
  4. Clamp loMin to 0 whenever it goes negative.
  5. After the scan, return True if loMin == 0, else False.

Complexity

Time: O(n) — Space: O(1)

Code

def checkValidString(s: str) -> bool:
    loMin, hiMax = 0, 0

    for c in s:
        if c == "(":
            loMin += 1
            hiMax += 1
        elif c == ")":
            loMin -= 1
            hiMax -= 1
        else:
            loMin -= 1
            hiMax += 1

        if hiMax < 0:
            return False
        loMin = max(loMin, 0)

    return loMin == 0

Code Explanation

How the solution above works, piece by piece.

  1. Instead of trying every choice for *, track the range of possible open-bracket counts: loMin (fewest) to hiMax (most).
  2. ( raises both; ) lowers both; * could be ), empty or (, so it lowers loMin and raises hiMax.
  3. If even the maximum drops below 0, there are too many ) no matter what → False.
  4. A negative minimum just means some choices were bad, so clamp loMin at 0.
  5. At the end, valid if 0 open brackets is possible: loMin == 0. O(n) time, O(1) space.

Visual Walkthrough

Running it on s = "(*))":

trace11'('open count could be 1..102'*'open count could be 0..201')'open count could be 0..1 (clamped at 0)00')'open count could be 0..0 (clamped at 0) → min is 0 → True
Each row is the [min, max] range of unmatched ( after that character. The range never goes fully negative, and 0 is inside it at the end, so the string can be valid (the * acts as ().

Video Explanation

NeetCode — LeetCode 678