Given a string s
containing only three types of characters: '('
, ')'
and '*'
, return true
if s
is valid.
The following rules define a valid string:
'('
must have a corresponding right parenthesis ')'
.')'
must have a corresponding 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 ""
.Example 1:
Input: s = "()" Output: true
Example 2:
Input: s = "(*)" Output: true
Example 3:
Input: s = "(*))" Output: true
Constraints:
1 <= s.length <= 100
s[i]
is '('
, ')'
or '*'
.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force approach to this parenthesis problem involves exploring every possible combination of open, close, or wildcard characters. We treat each wildcard as either an open or a close parenthesis and try all combinations. Then, we check if each combination results in a 'valid' arrangement of parentheses.
Here's how the algorithm would work step-by-step:
def valid_parenthesis_string_brute_force(input_string):
possible_strings = []
def generate_combinations(current_string, index):
if index == len(input_string):
possible_strings.append(current_string)
return
if input_string[index] == '*':
# Try replacing '*' with '('
generate_combinations(current_string + '(', index + 1)
# Try replacing '*' with ')'
generate_combinations(current_string + ')', index + 1)
else:
generate_combinations(current_string + input_string[index], index + 1)
generate_combinations("", 0)
def is_valid(check_string):
balance = 0
for char in check_string:
if char == '(':
balance += 1
elif char == ')':
balance -= 1
if balance < 0:
return False
return balance == 0
# Check if ANY of the generated strings are valid
for possible_string in possible_strings:
if is_valid(possible_string):
return True
return False
The key idea is to keep track of the possible balance of opening and closing parenthesis. We consider how many unmatched open parenthesis we *could* have, and how few we *must* have, after reading each character. If the range between those two values ever becomes invalid, we know the string is invalid.
Here's how the algorithm would work step-by-step:
def is_valid_parenthesis_string(input_string):
minimum_open_count = 0
maximum_open_count = 0
for character in input_string:
if character == '(':
minimum_open_count += 1
maximum_open_count += 1
elif character == ')':
minimum_open_count -= 1
maximum_open_count -= 1
else:
minimum_open_count -= 1
maximum_open_count += 1
# Too many closing parenthesis, invalid
if maximum_open_count < 0:
return False
# Resetting to zero, since negatives don't matter
minimum_open_count = max(minimum_open_count, 0)
# String is valid if all open parenthesis are matched
return minimum_open_count == 0
Case | How to Handle |
---|---|
Null or empty string input | Return true immediately because an empty string is considered valid. |
String containing only '*' | Return true because '*' can be treated as an empty string, resulting in a valid string. |
String containing only '(' | Return false because there are unmatched opening parentheses. |
String containing only ')' | Return false because there are unmatched closing parentheses. |
String with deeply nested '(' before any ')' or '*' | The solution needs to ensure that the available '*' characters can balance these deep nestings. |
String with many ')' at the beginning before any '(' or '*' | The solution should handle the case where right parentheses appear before left parentheses with the help of '*'. |
String with a large number of '*' characters interspersed among '(' and ')' | The solution should explore all possibilities of '*' which may lead to potential time complexity issues and should be handled carefully. |
Very long input string to check for performance | An efficient algorithm with linear time complexity, like using a balance counter or dynamic programming with memoization, is necessary to avoid timeouts. |