Balanced Bracket Check With Wildcard Characters
Solve this Problems containing '(', ')', and '?' — where each '?' can stand in for '(', for ')', or for nothing at all — determine whether some assignment of the wildcards makes the string a balanced bracket sequenceBalancedEvery '(' has a matching ')' later in the string, and every ')' has a matching '(' earlier — with '?' free to become whichever character (or nothing) makes that possible..
Trying every combination of wildcard assignments is correct but explodes exponentially. The key insight that avoids it: instead of picking one interpretation for each '?' up front, track the entire range of open-bracket counts that remain simultaneously possible as the string is scanned — the fewest opens achievable so far, and the most. A '?' simply widens that range by one on each side rather than forcing a choice. As long as 0 stays reachable within the range by the end, some valid assignment exists — found in a single linear pass instead of a search tree.
Test Case 1:
Test Case 2:
Test Case 3:
Constraints
- ◆
1 ≤ s.length ≤ 20 - ◆
s consists only of the characters '(', ')', and '?' - ◆
'?' may stand in for '(', for ')', or for nothing at all (an empty character)
Try the Dry Run
Don't just read the solution — watch it execute, one step at a time.
Approach & Solutions
Brute Force — Try Every Interpretation of Every Wildcard
BruteWalk the string recursively, tracking how many '(' are currently open. A '(' always increments the open count; a ')' always decrements it (failing immediately if the count goes negative — too many closes). A '?' branches three ways: try it as '(', try it as ')', and try it as empty — succeeding if any one of the three leads to a fully valid string. This explores every possible assignment of every wildcard, so it's always correct, but with up to n wildcards, that's up to 3ⁿ combinations in the worst case.
O(3ⁿ)O(n)1class Solution {
2 public boolean checkValidString(String s) {
3 return dfs(s, 0, 0);
4 }
5
6 private boolean dfs(String s, int i, int open) {
7 if (open < 0) return false;
8 if (i == s.length()) return open == 0;
9 char c = s.charAt(i);
10 if (c == '(') return dfs(s, i + 1, open + 1);
11 if (c == ')') return dfs(s, i + 1, open - 1);
12 return dfs(s, i + 1, open + 1) || dfs(s, i + 1, open - 1) || dfs(s, i + 1, open);
13 }
14}Optimal — Track a Range of Possible Open Counts
OptimalInstead of branching on every wildcard, track the entire range of open-bracket counts that are simultaneously still possible: lo (the fewest opens if every wildcard so far became ')' or empty) and hi (the most opens if every wildcard became '('). '(' shifts both bounds up by one; ')' shifts both down by one; '?' shifts hi up and lo down (since it could be either). If hi ever dips below 0, even the most generous interpretation has too many closes — fail immediately. If lo dips below 0, that's fine — it just means the least generous interpretation over-closed, but some wildcard earlier could have been empty instead, so clamp lo back to 0 rather than failing. The string is valid if 0 is still within range at the end (lo reaches exactly 0).
O(n)O(1)1class Solution {
2 public boolean checkValidString(String s) {
3 int lo = 0, hi = 0;
4 for (char c : s.toCharArray()) {
5 if (c == '(') { lo++; hi++; }
6 else if (c == ')') { lo--; hi--; }
7 else { lo--; hi++; }
8 if (hi < 0) return false;
9 if (lo < 0) lo = 0;
10 }
11 return lo == 0;
12 }
13}