A parentheses string is a non-empty string consisting only of '('
and ')'
. It is valid if any of the following conditions is true:
()
.AB
(A
concatenated with B
), where A
and B
are valid parentheses strings.(A)
, where A
is a valid parentheses string.You are given a parentheses string s
and a string locked
, both of length n
. locked
is a binary string consisting only of '0'
s and '1'
s. For each index i
of locked
,
locked[i]
is '1'
, you cannot change s[i]
.locked[i]
is '0'
, you can change s[i]
to either '('
or ')'
.Return true
if you can make s
a valid parentheses string. Otherwise, return false
.
Example 1:
Input: s = "))()))", locked = "010100" Output: true Explanation: locked[1] == '1' and locked[3] == '1', so we cannot change s[1] or s[3]. We change s[0] and s[4] to '(' while leaving s[2] and s[5] unchanged to make s valid.
Example 2:
Input: s = "()()", locked = "0000" Output: true Explanation: We do not need to make any changes because s is already valid.
Example 3:
Input: s = ")", locked = "0" Output: false Explanation: locked permits us to change s[0]. Changing s[0] to either '(' or ')' will not make s valid.
Example 4:
Input: s = "(((())(((())", locked = "111111010111" Output: false Explanation: locked permits us to change s[6] and s[8]. We change s[6] and s[8] to ')' to make s valid.
Constraints:
n == s.length == locked.length
1 <= n <= 105
s[i]
is either '('
or ')'
.locked[i]
is either '0'
or '1'
.This problem asks whether a given parentheses string s
can be made valid by changing at most some of its characters, based on the constraints given by a locked
string. A valid parentheses string is one where every opening parenthesis has a corresponding closing parenthesis in the correct order.
The solution employs a clever greedy approach using two scans of the input string. It leverages the fact that if a string can be made valid, it must satisfy two conditions:
Sufficient opening parentheses from left to right: When scanning from left to right, the number of opening parentheses ((
) or changeable parentheses (locked as '0') must always be greater than or equal to the number of closing parentheses ()
. If at any point, we encounter more closing parentheses than available opening/changeable parentheses, the string cannot be made valid.
Sufficient closing parentheses from right to left: Similarly, when scanning from right to left, the number of closing parentheses ()
or changeable parentheses must always be greater than or equal to the number of opening parentheses ((
).
Algorithm:
Length Check: The function first checks if the length of s
is odd. If it is, it's impossible to make the string valid, so it returns false
.
Left-to-Right Scan: It initializes a counter x
to 0. It iterates through s
from left to right. For each character:
locked
is '0' (meaning it's changeable), increment x
.x
is greater than 0, decrement x
(we use an available opening parenthesis to match it).x
is 0, it means there's no matching opening parenthesis, so the string cannot be made valid, and it returns false
.Right-to-Left Scan: It resets x
to 0 and iterates through s
from right to left. This scan performs the same logic as the left-to-right scan but in reverse to ensure there are enough closing parentheses for each opening parenthesis.
Return True: If both scans complete without returning false
, it means the string can be made valid, so the function returns true
.
Time Complexity: O(n), where n is the length of the strings s
and locked
. The algorithm performs two linear scans of the strings.
Space Complexity: O(1). The algorithm uses only a constant amount of extra space to store the counter x
.
Python:
class Solution:
def canBeValid(self, s: str, locked: str) -> bool:
n = len(s)
if n % 2 != 0: #Odd length strings cannot be valid
return False
def check(direction):
count = 0
for i in range(n):
idx = i if direction == 1 else n - 1 - i
char = s[idx]
locked_char = locked[idx]
if char == '(' or locked_char == '0':
count +=1
elif count > 0:
count -= 1
else:
return False
return True
return check(1) and check(-1)
Java:
class Solution {
public boolean canBeValid(String s, String locked) {
int n = s.length();
if (n % 2 != 0) return false;
boolean check(int direction) {
int count = 0;
for (int i = 0; i < n; i++) {
int idx = (direction == 1) ? i : n - 1 - i;
char c = s.charAt(idx);
char l = locked.charAt(idx);
if (c == '(' || l == '0') count++;
else if (count > 0) count--;
else return false;
}
return true;
}
return check(1) && check(-1);
}
}
The other languages (C++, Go) would have very similar implementations, following the same algorithmic steps. The improved code emphasizes readability by using a helper function check
to encapsulate the left-to-right and right-to-left scans, making the main function cleaner.