Check if a Parentheses String Can Be Valid

Updated on 20 May, 2025
Check if a Parentheses String Can Be Valid header image

Problem Statement

Given a string s comprised solely of the characters '(' and ')', we need to determine if it's possible to transform this string into a valid parentheses string according to typical programming standards (balanced parentheses).

The string's transformation ability is governed by another string locked that has the same length as s. This string locked is a binary string, consisting only of '0's and '1's. The character at each position of locked signals whether the corresponding character in s can be changed:

  • If locked[i] is '1', the character s[i] cannot be modified.
  • If locked[i] is '0', you are free to change s[i] to either '(' or ')'.

The primary task is to determine if there is any possible combination of changes that can be made (where allowed) to make s into a valid parentheses string, wherein valid is defined as:

  • Completely balanced - every opening bracket '(' has a corresponding closing bracket ')'.
  • Properly nested - brackets are correctly aligned in the typical (A) format.

Examples

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'.

Approach and Intuition

  1. Initialize Counter for Flexibility: Create a balance tracker—for open and close parentheses. Also, count how many positions are flexible (i.e., marked by '0' in the locked string).

  2. First Pass - Apply Fixed Characters: Traverse the string s and update the balance tracker.

    • If you encounter a fixed '(' (where locked[i] is '1'), increment the balance because you have an unpaired opening bracket.
    • If you encounter a fixed ')' (where locked[i] is '1'), decrement the balance because you have a closing bracket that needs to match an opening.
    • Track the count of changeable positions ('0' in locked) separately as changeable.
  3. Balance Check during Traversal:If at any point during this traversal the balance goes negative, it indicates that there are more closing brackets than opening—with all options so far considered, this can't be balanced later and is invalid immediately.

  4. Second Pass - Assess Flexibility's Impact:

    • After assessing the fixed characters, see if the remaining changeable positions can balance out any misalignment.
    • Since each changeable position can swing the balance by 1 (either by adding a '(' or removing a ')'), ensure that half of these changeable slots can compensate for any deficit (if the final balance count is greater than zero) or offset any excess (if balance is less than zero).
  5. Achieving Balance:

    • If, after consideration of all fixed characters and potential flexible adjustments, the absolute final balance counts can be made zero through available flexible spots, the string can be transformed into a valid one. Otherwise, it can't.

This approach uses the dual traversal to assess restrictions and potentials separately, ensuring a thorough evaluation of all the positions' impact on the string's validity. The complexity is optimized by ensuring only necessary checks and counts are done predominantly in a linear pass through the string.

Solutions

  • C++
  • Java
  • Python
cpp
class Solution {
public:
    bool validateParenthesis(string s, string lockStatus) {
        int n = s.length();
        if (n % 2 != 0) return false;

        int open = 0, free = 0;
        for (int i = 0; i < n; i++) {
            if (lockStatus[i] == '0') {
                free++;
            } else if (s[i] == '(') {
                open++;
            } else if (s[i] == ')') {
                if (open > 0) open--;
                else if (free > 0) free--;
                else return false;
            }
        }

        int checks = 0;
        for (int i = n - 1; i >= 0; i--) {
            if (lockStatus[i] == '0') {
                checks--;
                free--;
            } else if (s[i] == '(') {
                checks++;
            } else if (s[i] == ')') {
                checks--;
            }

            if (checks > 0) return false;
        }

        return true;
    }
};

The provided C++ solution involves determining if a string of parentheses is valid, considering additional constraints specified by a "lockStatus" string. Here’s how the validation process works:

  • First, check if the length of the string s is even, as odd-length strings can't form valid pairs of parentheses and return false immediately if odd.

  • Initialize counters for open parentheses and free slots (characters that can be either ( or ) depending on need). Iterate through the string:

    • Increment the free counter if the character at index i in lockStatus is '0' (unlocked).
    • If the character in s is '(', increment the open counter if it's locked, otherwise it's already accounted for in free.
    • For ')' in a locked position, decrement open if any are open, or use a free slot if available. If neither is possible, the string can't be valid, return false.
  • Perform a reverse check to ensure the sequence remains valid from the end to the beginning:

    • Decrement the free and adjust the balance checks whenever encountering a free slot going backwards.
    • Adjust the balance for every parenthesis. If the balance becomes positive at any stage, return false since more opening parentheses than closing would be present from that position back to the start.
  • If all checks are completed without issues, the function returns true, indicating the string is valid based on given conditions. This solution ensures both forward and reverse validation to account for all scenarios imposed by locked and unlocked positions in the string.

java
class Solution {

    public boolean validateParentheses(String inputStr, String lockPattern) {
        int strLength = inputStr.length();

        // Return false if the string's length is not even.
        if (strLength % 2 != 0) {
            return false;
        }

        int openCount = 0;
        int freeSlots = 0;

        // Loop to evaluate the conditions for locked and unlocked characters.
        for (int idx = 0; idx < strLength; idx++) {
            if (lockPattern.charAt(idx) == '0') {
                freeSlots++;
            } else if (inputStr.charAt(idx) == '(') {
                openCount++;
            } else if (inputStr.charAt(idx) == ')') {
                if (openCount > 0) {
                    openCount--;
                } else if (freeSlots > 0) {
                    freeSlots--;
                } else {
                    return false;
                }
            }
        }

        int balanceCheck = 0;
        // Reverse loop to match open brackets against free slots.
        for (int idx = strLength - 1; idx >= 0; idx--) {
            if (lockPattern.charAt(idx) == '0') {
                balanceCheck--;
                freeSlots--;
            } else if (inputStr.charAt(idx) == '(') {
                balanceCheck++;
                openCount--;
            } else if (inputStr.charAt(idx) == ')') {
                balanceCheck--;
            }
            if (balanceCheck > 0) {
                return false;
            }
        }

        if (openCount > 0) {
            return false;
        }

        return true;
    }
}

This Java solution checks whether a given string containing parentheses is valid based on an additional "lock pattern". The string must have an equal number of opening and closing parentheses, and the lock pattern controls which characters in the string can be considered for swapping to achieve balance.

  • The method validateParentheses accepts two strings, inputStr and lockPattern, where inputStr is composed of parentheses characters and lockPattern represents locked (1) or unlocked (0) positions.
  • The length of the inputStr is checked to be even. If it's odd, the method immediately returns false since an equal number of open and close brackets cannot be achieved.
  • Variables openCount and freeSlots are used to track the number of open parentheses and positions that can change respectively.
  • The first for-loop scans inputStr in conjunction with lockPattern. If a position is locked with an open paren, openCount increments; if it's a close paren and there's a matching open, openCount decrements, or freeSlots is used if available. If conditions aren't met, false is returned.
  • A reverse loop then ensures that there are no extra open parentheses left unbalanced at the end of the string, by decrementing for every free slot and adjusting the balance for locked positions.
  • If openCount or balanceCheck indicate an imbalance at the end of the method, false is returned.
  • If all checks are satisfied, true is returned, indicating the string can be rearranged to be a valid parentheses sequence per the rules of the lock pattern.
python
class Solution:
    def validateParentheses(self, sequence: str, flag_sequence: str) -> bool:
        n = len(sequence)
        if n % 2 != 0:
            return False
        open_count = 0
        free_positions = 0
        
        for idx in range(n):
            if flag_sequence[idx] == "0":
                free_positions += 1
            elif sequence[idx] == "(":
                open_count += 1
            elif sequence[idx] == ")":
                if open_count > 0:
                    open_count -= 1
                elif free_positions > 0:
                    free_positions -= 1
                else:
                    return False
        
        closing_brackets = 0
        for idx in range(n - 1, -1, -1):
            if flag_sequence[idx] == "0":
                closing_brackets -= 1
                free_positions -= 1
            elif sequence[idx] == "(":
                closing_brackets += 1
            elif sequence[idx] == ")":
                closing_brackets -= 1
            if closing_brackets > 0:
                return False

        if open_count > 0:
            return False

        return True

The Python function validateParentheses checks whether a given string of parentheses, represented by the sequence sequence, is valid based on the constraints provided in flag_sequence. The function follows these steps to determine validity:

  • The length of sequence is verified to be even. An odd length immediately returns False because a valid parentheses string must have paired opening and closing parentheses.
  • Initialize counters for open parentheses (open_count) and free positions, which are slots in the string that are not constrained (free_positions). Free positions are determined by entries in flag_sequence that are "0".
  • Iterate through each character in sequence:
    • Increase free_positions whenever an unconstrained slot is encountered.
    • Modify open_count based on the type of parenthesis:
      • Increase for an open parenthesis if it's not a free position.
      • Decrease for a closed parenthesis if available open parenthesis is present. Otherwise, try to use a free position to balance out unmatched parentheses, failing which return False.
  • Verify the overall balance of parentheses from the end of the sequence to the start, using a new counter closing_brackets. This step checks for excess open parentheses that cannot be closed. If any mismatch is found, return False.
  • Ensure that all opened parentheses have been correctly closed by checking if open_count is greater than zero upon complete processing.

This method returns True if the sequence adheres to all rules and constraints for a valid parentheses string, otherwise it returns False. Each operation and conditional check efficiently manages and verifies both matched and unmatched parentheses and their placement based on the constraints provided.

Comments

No comments yet.