Valid Parenthesis String
Medium Top 250
Problem Description
Given a string s containing only three types of characters: '(', ')' and '*', return true if s is valid.
The following rules define a valid string:
- Any left parenthesis
'('must have a corresponding right parenthesis')'. - Any right parenthesis
')'must have a corresponding left parenthesis'('. - Left parenthesis
'('must go before the corresponding right parenthesis')'. '*'could be treated as a single right parenthesis')'or a single left parenthesis'('or an empty string"".
Examples
Example 1:Input: s = ”()” Output: true
Input: s = ”(*)” Output: true
Input: s = ”(*))” Output: true
Constraints
1 ≤ s.length ≤ 100s[i]is'(',')'or'*'.
Greedy Balance Ranges
A standard parenthesis validator tracks the “balance” of open parentheses (increment on (, decrement on )). The balance must never drop below 0, and must end at exactly 0.
Because * can act as (, ), or "", the balance is not a single integer, but a range of possible balances [minOpen, maxOpen]:
- If we see
(, bothminOpenandmaxOpenincrement by 1. - If we see
), bothminOpenandmaxOpendecrement by 1. - If we see
*:- It can act as
)(decrementsminOpen) or((incrementsmaxOpen). Thus:minOpen -= 1andmaxOpen += 1.
- It can act as
Rules:
- Invalidity Check: If at any point
maxOpenbecomes negative, the string is invalid (we have too many)characters, and even converting all*to(cannot balance them). Returnfalse. - Lower Bound Restraint: The balance of open parentheses cannot drop below 0. If
minOpenbecomes negative, resetminOpen = 0(this means we greedily choose to treat some*as empty or(instead of)to prevent going negative). - End Condition: The string is valid if the range of possible balances at the end contains
0, which meansminOpen == 0at the end.
Solution: Range Tracking
class Solution {
public boolean checkValidString(String s) {
int minOpen = 0;
int maxOpen = 0;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == '(') {
minOpen++;
maxOpen++;
} else if (c == ')') {
minOpen--;
maxOpen--;
} else { // c == '*'
minOpen--;
maxOpen++;
}
if (maxOpen < 0) return false;
if (minOpen < 0) minOpen = 0; // reset lower bound
}
return minOpen == 0;
}
}class Solution:
def checkValidString(self, s: str) -> bool:
min_open = 0
max_open = 0
for char in s:
if char == '(':
min_open += 1
max_open += 1
elif char == ')':
min_open -= 1
max_open -= 1
else: # char == '*'
min_open -= 1
max_open += 1
if max_open < 0:
return False
if min_open < 0:
min_open = 0
return min_open == 0#include <string>
#include <algorithm>
class Solution {
public:
bool checkValidString(std::string s) {
int minOpen = 0;
int maxOpen = 0;
for (char c : s) {
if (c == '(') {
minOpen++;
maxOpen++;
} else if (c == ')') {
minOpen--;
maxOpen--;
} else {
minOpen--;
maxOpen++;
}
if (maxOpen < 0) return false;
if (minOpen < 0) minOpen = 0;
}
return minOpen == 0;
}
};Complexity Analysis:
- Time Complexity: O(N) where N is the length of the string
s. - Space Complexity: O(1) space.