This is the classic valid parentheses problem with one addition: the wildcard *. Without *, the answer comes from a single counter: increment on (, decrement on ), and check that it never goes negative and ends at zero.
The * complicates this because at each * position there are three choices: treat it as (, as ), or as nothing. Trying all combinations explodes exponentially. The real question is whether there exists some assignment of wildcards that makes the string valid, without enumerating every assignment.
We don't need the exact assignment. We only need to know whether the range of possible open-parenthesis counts at the end includes zero. By tracking the minimum and maximum possible count of unmatched ( at each position, validity falls out of a single pass.
1 <= s.length <= 100: n is small enough that an O(n^2) DP passes, and an O(n) greedy is comfortable. The small bound is what lets the brute force and DP approaches below run in time at all.s[i] is '(', ')' or '*': only three character types, so there are no other cases to handle.Try every possibility. At each *, branch into three recursive calls that treat it as (, as ), or as the empty string. For ( and ), adjust the open-parenthesis counter as usual.
If any branch reaches a valid state (counter equals zero at the end and never went negative), the string is valid. This is an exhaustive search over all wildcard assignments.
(, recurse with count + 1.), recurse with count - 1.*, try all three: recurse with count + 1, count - 1, and count unchanged. Return true if any branch returns true.Loading animation...
* character, we branch into 3 recursive calls. In the worst case where every character is *, this gives us 3^n total calls.The state at any point is fully determined by the current index and the open-parenthesis count, yet this recursion recomputes the same (index, openCount) states many times. Caching those results removes the repeated work.
The recursion from Approach 1 has overlapping subproblems. The state is (index, openCount), and since index ranges from 0 to n and openCount ranges from 0 to n, there are at most O(n^2) distinct states. Caching their results removes the exponential blowup.
Each state answers one question: starting at position index with openCount unmatched open parens, can the rest of the string be completed validly? Once that question is answered, every later call with the same state reads the stored answer instead of recursing again.
Loading animation...
The next approach replaces the table entirely. Instead of tracking every reachable open count individually, it tracks only the range of reachable open counts, which collapses the state down to two variables.
This problem is solvable in O(n) time and O(1) space by tracking only two numbers instead of the full set of reachable open counts: the minimum and maximum possible count of unmatched open parentheses.
Without wildcards, there is exactly one open count at each position. With wildcards, there is a range, and that range stays contiguous because every reachable count between the minimum and maximum is also reachable (any single * you flipped from ( to ) shifts the total by exactly 1). So we maintain low (minimum possible open count) and high (maximum possible open count):
( increases both by 1) decreases both by 1* decreases low by 1 and increases high by 1Two checks guard the range. If high drops below 0, even treating every * as ( leaves an unmatched ), so return false. And low can never represent fewer than zero unmatched opens, so we clamp it at 0. At the end, if low is 0, zero is within the range and a valid assignment exists.
Clamping low at 0 is safe because a prefix that would push the minimum below 0 can always assign one more * (or () as a non-closing character to keep the count at 0, so 0 stays a reachable open count. The validity test is whether 0 lies in the final range [low, high]. The high < 0 early exit already rules out the case where 0 is above the range, and low is clamped at 0, so 0 is in range exactly when low == 0.
low = 0 and high = 0.(: increment both low and high.): decrement both low and high.*: decrement low by 1, increment high by 1.high < 0: return false (too many ) even with all * as ().low to at least 0 (can't have negative unmatched ().low == 0.Loading animation...