Shubham Sourabhthe room · director's cutplain résumé
Room 04 · The Grind — Greedy

Valid Parenthesis String

MEDIUM✓ CHIP-TIMEDLC #678 — FULL STATEMENT

The briefingthe full drill, in my own words

The drill: A string of '(', ')' and '*' where every '*' can freely stand in for '(', ')', or nothing at all. Decide whether some assignment of the wildcards makes the whole string balanced.

A string mixes three characters: '(', ')', and '*'. Each '*' is a wildcard that can independently be treated as an open paren, a close paren, or nothing at all — an empty placeholder that vanishes from the string.

The task is deciding whether at least one way of resolving every wildcard turns the whole string into a properly balanced sequence of parentheses, where every open has a later matching close and nothing closes before it opens.

Different wildcards can be resolved differently from each other; the only requirement is that some single consistent assignment across all of them produces balance.

  • the string contains only the characters '(', ')', and '*'
  • each '*' independently resolves to '(', ')', or an empty string
  • a valid resolution must keep every prefix from closing more than it opened
  • the verdict is whether any resolution at all achieves full balance
EX 01
s = "()"
→ true
already balanced, no wildcards
EX 02
s = "(*)"
→ true
star can be empty
EX 03
s = "(*"
→ true
star stands in for the missing close

The hintstake only what you need

HINT 1 THE NUDGE

Trying every wildcard assignment is exponential. What's the smallest summary of 'how many opens am I currently holding' that still lets you decide validity?

HINT 2 THE STRUCTURE

Instead of one open-count, track a whole range: the lowest and highest number of opens still reachable at this point, given every wildcard choice made so far.

HINT 3 ONE STEP FROM THE ANSWER

'(' shifts both ends of the range up, ')' shifts both down, '*' widens the range by one on each side. Clamp the low end at zero as you go; if the high end ever dips below zero, no assignment saves it. Balanced iff the range still contains zero at the end.

Coach's boardthe pattern, step by step

THE OPEN-COUNT RANGEPATTERN · GREEDY — LO/HI RANGEs = "(*))"
(
*
)
)
OPEN RANGE [lo, hi]
lo → 0
hi → 0
STEP 1

s = "(*))". Each '*' can be '(', ')', or nothing — track the whole range of possible open-paren counts instead of one number.

STEP 1 / 7 · ← → WORK TOO

The splitstwo paces, two languages

grind/valid-parenthesis-string.py — RACE PACE
LANG ▸
PACE ▸
class Solution:
    def checkValidString(self, s: str) -> bool:
        lo = hi = 0
        for c in s:
            if c == "(":
                lo += 1
                hi += 1
            elif c == ")":
                lo -= 1
                hi -= 1
            else:
                lo -= 1
                hi += 1
            if hi < 0:
                return False
            lo = max(lo, 0)
        return lo == 0
TIME O(N)SPACE O(1)PYTHON · RACE PACE · 17 LN

✓ CHIP-TIMEDALL 4 SOLUTIONS RAN GREEN AGAINST SELF-AUTHORED CASES IN CI · JDK 21 · CPYTHON 3.12 · NOTHING PUBLISHES RED