open-count range after each character
public boolean checkValidString(String s) { int leftMin = 0; // range of open counts int leftMax = 0; for (char c : s.toCharArray()) { if (c == '(') { leftMin++; leftMax++; } else if (c == ')') { leftMin--; leftMax--; } else { leftMin--; leftMax++; } if (leftMax < 0) { return false; } if (leftMin < 0) { leftMin = 0; // can't have negative opens } } return leftMin == 0;}
- c
- —
- leftMin
- 0
- leftMax
- 0
Why it works
- Brute force tries all three roles for every
*: O(3ᵏ) for k stars. A DP over (index, open count) brings it to O(n²). - Track a range, not a choice. After each character, the number of unmatched
(could be any value in[leftMin, leftMax], depending on how the stars were used.(shifts the range up,)shifts it down,*widens it both ways. - Two numbers per character: O(n) time, O(1) space. If even
leftMaxdrops below 0, too many)arrived: fail at once. At the end, balanced is possible exactly when 0 is in the range, that isleftMin == 0. - Pitfall: clamp
leftMinat 0. A negative open count is impossible; it just means the choices that made it are ruled out. Without the clamp,"(*)"ends atleftMin = -1and wrongly fails. - Order matters:
"*)("has balanced counts but fails, because the last(leavesleftMin = 1. Related: Valid Parentheses (20) is the version without stars, a plain counter or stack.