Talentd
FleetCode
Make and follow sheets.Sign in

Check if a Parentheses String Can Be Valid solution and explanation

MediumGreedyStackString6 min read45.1% acceptanceUpdated 9 Apr 2026
Check if a Parentheses String Can Be Valid - Leetcode 2116 - PythonNeetCodeIO · 20:41 · 18K viewsWatch on YouTube
9 more videos on this problem
  1. Check if a Parentheses String Can Be Valid - Leetcode 2116 - PythonNeetCodeIO · 20:41 · 18K views
  2. Check if a Parentheses String Can Be Valid | Detailed Intuition | 2 Approaches | Leetcode 2116 | MIKcodestorywithMIK · 52:54 · 13K views
  3. Check if a Parentheses String Can Be Valid | Leetcode 2116Techdose · 25:49 · 7K views
  4. Leetcode 2116 Check if a Parentheses String Can Be Valid - Interesting Problem, Interesting SolutionAlgorithmicIQ · 33:43 · 3K views
  5. 2116. Check if a Parentheses String Can Be Valid | Stack | Constant SpaceAryan Mittal · 35:04 · 2K views
  6. 2116. Check if a Parentheses String Can Be Valid | leetcode daily challenge | dsa | shashcode | javaShashCode · 20:04 · 2K views
  7. 2116. Check if a Parentheses String Can Be Valid (Leetcode Medium)Programming Live with Larry · 37:10 · 1K views
  8. Check if a Parentheses String Can Be Valid | Leetcode - 2116Algorithms Made Easy · 18:54 · 720 views
  9. Daily Leetcode #1748: Jan 12, 2025 - 2116. Check if a Parentheses String Can Be ValidProgramming Live with Larry · 23:01 · 333 views
  10. Leetcode | 2116 Check if a Parentheses String Can Be Valid | Java | Leetcode Daily Challenge| StackCakot Coding · 21:52 · 204 views

The problem

A parentheses string is a non-empty string consisting only of '(' and ')'. It is valid if any of the following conditions is true:

  • It is ().
  • It can be written as AB (A concatenated with B), where A and B are valid parentheses strings.
  • It can be written as (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,

  • If locked[i] is '1', you cannot change s[i].
  • But if 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.

Constraints:

  • n == s.length == locked.length
  • 1 <= n <= 105
  • s[i] is either '(' or ')'.
  • locked[i] is either '0' or '1'.

Approach overview

Problem Overview: You receive a parentheses string s and a binary string locked. If locked[i] = 1, the character at s[i] cannot change. If locked[i] = 0, you may flip it between ( and ). The task is to determine whether the string can be transformed into a valid parentheses sequence.

A quick observation: a valid parentheses string must have even length. If n is odd, the answer is immediately false.

Approach 1: Using Hashing for Lookup (Greedy Balance Tracking) (O(n) time, O(n) space)

This approach tracks locked and unlocked positions while scanning the string. Use a hash-based structure (such as sets or maps) to record indices of flexible characters (locked[i] = 0) and unmatched parentheses. While iterating through the string, treat unlocked characters as potential fixes for imbalance. When you encounter an extra closing parenthesis, look up a previously stored unlocked index and convert it to an opening parenthesis. If no such index exists, the sequence cannot be fixed. A second pass ensures remaining unmatched openings can be balanced by later unlocked positions. The hash lookup provides constant-time checks for available replacements.

This strategy models the problem as flexible corrections in the sequence. It’s helpful when reasoning about which indices can be swapped or reassigned dynamically. It also makes the matching process explicit by storing candidate positions.

Approach 2: Sorting and Two-Pointer Technique (Greedy Range Validation) (O(n) time, O(1) space)

The optimal approach treats unlocked characters as wildcards and validates feasibility using two directional passes. Scan from left to right while maintaining a balance counter. Treat unlocked characters as ( if needed to prevent the balance from dropping below zero. This ensures you never have more closing parentheses than possible openings.

Next, run a reverse scan (right to left). This time treat unlocked characters as ) when necessary to ensure the suffix can be balanced. Conceptually, this works like a two-pointer validation across the string: the forward pass guarantees prefixes are fixable, and the backward pass guarantees suffixes are fixable.

The key insight is that unlocked characters can be assigned optimally depending on which direction you scan. No explicit stack is required; simple counters track the feasible range of open parentheses. This greedy method runs in linear time and constant space, which is ideal for large inputs.

Problems like this commonly appear in discussions about string processing, stack-based validation, and greedy algorithms. Understanding how flexible characters affect balance constraints is the main trick.

Recommended for interviews: The greedy two-pass approach is what interviewers typically expect. It demonstrates you understand the structural property of valid parentheses and can reason about constraints without brute-force search. A hash-based or stack-style solution shows the matching intuition, but the O(n) time and O(1) space greedy validation signals stronger algorithmic thinking.

Approach 1: Using Hashing for Lookup

This approach leverages a hash set or hash map to keep track of elements that have been encountered. By using these data structures, we can achieve an average time complexity of O(1) for insertions and lookups, which is useful for problems that require checking the existence of an element.

def findTarget(nums, target):
    hash_set = set()
    for num in nums:
        if target - num in hash_set:
            return True
        hash_set.add(num)
    return False

nums = [10, 15, 3, 7]
target = 17
print(findTarget(nums, target))
import java.util.HashSet;

public class Main {
    public static boolean findTarget(int[] nums, int target) {
        HashSet<Integer> hashSet = new HashSet<>();
        for (int num : nums) {
            if (hashSet.contains(target - num)) {
                return true;
            }
            hashSet.add(num);
        }
        return false;
    }

    public static void main(String[] args) {
        int[] nums = {10, 15, 3, 7};
        int target = 17;
        System.out.println(findTarget(nums, target) ? "True" : "False");
    }
}
#include <iostream>
#include <unordered_set>
#include <vector>

bool findTarget(const std::vector<int>& nums, int target) {
    std::unordered_set<int> hashSet;
    for (int num : nums) {
        if (hashSet.count(target - num)) {
            return true;
        }
        hashSet.insert(num);
    }
    return false;
}

int main() {
    std::vector<int> nums = {10, 15, 3, 7};
    int target = 17;
    std::cout << (findTarget(nums, target) ? "True" : "False") << std::endl;
    return 0;
}
function findTarget(nums, target) {
    const hashSet = new Set();
    for (let num of nums) {
        if (hashSet.has(target - num)) {
            return true;
        }
        hashSet.add(num);
    }
    return false;
}

const nums = [10, 15, 3, 7];
const target = 17;
console.log(findTarget(nums, target));
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define MAX 100000

bool hashSet[MAX] = {false};

bool findTarget(int* nums, int numsSize, int target) {
    for(int i = 0; i < numsSize; i++) {
        int complement = target - nums[i];
        if (complement >= 0 && hashSet[complement]) {
            return true;
        }
        hashSet[nums[i]] = true;
    }
    return false;
}

int main() {
    int nums[] = {10, 15, 3, 7};
    int target = 17;
    printf("%s", findTarget(nums, 4, target) ? "True" : "False");
    return 0;
}
using System;
using System.Collections.Generic;

class Program {
    public static bool FindTarget(int[] nums, int target) {
        HashSet<int> hashSet = new HashSet<int>();
        foreach (int num in nums) {
            if (hashSet.Contains(target - num)) {
                return true;
            }
            hashSet.Add(num);
        }
        return false;
    }

    static void Main() {
        int[] nums = {10, 15, 3, 7};
        int target = 17;
        Console.WriteLine(FindTarget(nums, target) ? "True" : "False");
    }
}

This Python solution uses a set to track numbers as they are iterated over, allowing O(1) membership checks.

Complexity

Time Complexity: O(N), where N is the number of array elements.
Space Complexity: O(N), required for the set.

Try this approach in the editor

Approach 2: Sorting and Two-Pointer Technique

This approach involves sorting the array and then using the two-pointer technique to find the two numbers that add up to the target. This technique is often effective when dealing with questions involving pairs, as it reduces the problem to O(N log N) due to sorting, followed by a linear scan.

def findTarget(nums, target):
    nums.sort()
    left, right = 0, len(nums) - 1
    while left < right:
        sum = nums[left] + nums[right]
        if sum == target:
            return True
        elif sum < target:
            left += 1
        else:
            right -= 1
    return False

nums = [10, 15, 3, 7]
target = 17
print(findTarget(nums, target))
import java.util.Arrays;

public class Main {
    public static boolean findTarget(int[] nums, int target) {
        Arrays.sort(nums);
        int left = 0, right = nums.length - 1;
        while (left < right) {
            int sum = nums[left] + nums[right];
            if (sum == target) {
                return true;
            } else if (sum < target) {
                left++;
            } else {
                right--;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        int[] nums = {10, 15, 3, 7};
        int target = 17;
        System.out.println(findTarget(nums, target) ? "True" : "False");
    }
}
#include <iostream>
#include <vector>
#include <algorithm>

bool findTarget(std::vector<int>& nums, int target) {
    std::sort(nums.begin(), nums.end());
    int left = 0, right = nums.size() - 1;
    while (left < right) {
        int sum = nums[left] + nums[right];
        if (sum == target) {
            return true;
        } else if (sum < target) {
            left++;
        } else {
            right--;
        }
    }
    return false;
}

int main() {
    std::vector<int> nums = {10, 15, 3, 7};
    int target = 17;
    std::cout << (findTarget(nums, target) ? "True" : "False") << std::endl;
    return 0;
}
function findTarget(nums, target) {
    nums.sort((a, b) => a - b);
    let left = 0, right = nums.length - 1;
    while (left < right) {
        let sum = nums[left] + nums[right];
        if (sum === target) {
            return true;
        } else if (sum < target) {
            left++;
        } else {
            right--;
        }
    }
    return false;
}

const nums = [10, 15, 3, 7];
const target = 17;
console.log(findTarget(nums, target));
#include <stdio.h>
#include <stdlib.h>

int compare(const void *a, const void *b) {
    return (*(int*)a - *(int*)b);
}

int findTarget(int* nums, int numsSize, int target) {
    qsort(nums, numsSize, sizeof(int), compare);
    int left = 0, right = numsSize - 1;
    while (left < right) {
        int sum = nums[left] + nums[right];
        if (sum == target) {
            return 1;
        } else if (sum < target) {
            left++;
        } else {
            right--;
        }
    }
    return 0;
}

int main() {
    int nums[] = {10, 15, 3, 7};
    int target = 17;
    printf("%s", findTarget(nums, 4, target) ? "True" : "False");
    return 0;
}
using System;

class Program {
    public static bool FindTarget(int[] nums, int target) {
        Array.Sort(nums);
        int left = 0, right = nums.Length - 1;
        while (left < right) {
            int sum = nums[left] + nums[right];
            if (sum == target) {
                return true;
            } else if (sum < target) {
                left++;
            } else {
                right--;
            }
        }
        return false;
    }

    static void Main() {
        int[] nums = {10, 15, 3, 7};
        int target = 17;
        Console.WriteLine(FindTarget(nums, target) ? "True" : "False");
    }
}

This Python approach sorts the list initially and then makes use of two pointers to identify a pair of numbers summing to the target.

Complexity

Time Complexity: O(N log N) for sorting, subsequent O(N) scan.
Space Complexity: O(1), assuming in-place sorting.

Try this approach in the editor

Approach 3: Greedy + Two Passes

We observe that a string of odd length cannot be a valid parentheses string because there will always be one unmatched parenthesis. Therefore, if the length of the string s is odd, return false immediately.

Next, we perform two passes.

The first pass goes from left to right, checking if all '(' parentheses can be matched by ')' or changeable parentheses. If not, return false.

The second pass goes from right to left, checking if all ')' parentheses can be matched by '(' or changeable parentheses. If not, return false.

If both passes complete successfully, it means all parentheses can be matched, and the string s is a valid parentheses string. Return true.

The time complexity is O(n), where n is the length of the string s. The space complexity is O(1).

Similar problems:

function canBeValid(s: string, locked: string): boolean {
    const n = s.length;
    if (n & 1) {
        return false;
    }
    let x = 0;
    for (let i = 0; i < n; ++i) {
        if (s[i] === '(' || locked[i] === '0') {
            ++x;
        } else if (x > 0) {
            --x;
        } else {
            return false;
        }
    }
    x = 0;
    for (let i = n - 1; i >= 0; --i) {
        if (s[i] === ')' || locked[i] === '0') {
            ++x;
        } else if (x > 0) {
            --x;
        } else {
            return false;
        }
    }
    return true;
}
func canBeValid(s string, locked string) bool {
	n := len(s)
	if n%2 == 1 {
		return false
	}
	x := 0
	for i := range s {
		if s[i] == '(' || locked[i] == '0' {
			x++
		} else if x > 0 {
			x--
		} else {
			return false
		}
	}
	x = 0
	for i := n - 1; i >= 0; i-- {
		if s[i] == ')' || locked[i] == '0' {
			x++
		} else if x > 0 {
			x--
		} else {
			return false
		}
	}
	return true
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Using Hashing for Lookup

Time Complexity: O(N), where N is the number of array elements.
Space Complexity: O(N), required for the set.

Sorting and Two-Pointer Technique

Time Complexity: O(N log N) for sorting, subsequent O(N) scan.
Space Complexity: O(1), assuming in-place sorting.

Greedy + Two Passes–

Complexity analysis

ApproachTimeSpaceWhen to Use
Hashing for Lookup (tracking unlocked indices)O(n)O(n)Useful when explicitly matching indices or demonstrating how flexible positions repair invalid pairs
Greedy Sorting / Two-Pointer ValidationO(n)O(1)Best general solution. Efficient for large inputs and commonly expected in interviews

Frequently asked questions

Is Check if a Parentheses String Can Be Valid easy or hard?

The problem is rated Medium on LeetCode. The main challenge is recognizing that unlocked characters act as wildcards and require validating both prefix and suffix feasibility. Once the greedy insight is clear, the implementation becomes straightforward.

Check if a Parentheses String Can Be Valid Python/Java solution

Python and Java implementations typically use the greedy two-pass technique. Maintain a balance counter during a forward scan and another during a backward scan. Each character is processed once per pass, making the implementation concise and efficient for large strings.

How to solve Check if a Parentheses String Can Be Valid in O(n)?

Perform two greedy scans. In the forward pass, ensure the number of closing parentheses never exceeds the possible openings by treating unlocked characters as '('. In the backward pass, ensure openings never exceed possible closings by treating unlocked characters as ')'. If both passes succeed, the string can be made valid in O(n) time.

What is the best approach for Check if a Parentheses String Can Be Valid?

The best approach is a greedy two-pass validation that scans the string from left to right and then from right to left. Unlocked characters are treated as flexible parentheses that can adjust to maintain balance. This ensures every prefix and suffix can be made valid. The algorithm runs in O(n) time with O(1) space.

Is Check if a Parentheses String Can Be Valid asked at Google/Amazon/Meta?

Parentheses validation and greedy balance problems frequently appear in interviews at companies like Google, Amazon, and Meta. Variants involving locked characters or flexible replacements test deeper understanding of greedy constraints and stack-based reasoning.

What data structure is used in Check if a Parentheses String Can Be Valid?

The optimized solution does not require a complex data structure and relies on simple counters. Alternative implementations may use stacks or hash-based sets to track indices of unmatched parentheses and unlocked positions for replacement.

What is the time complexity of Check if a Parentheses String Can Be Valid?

The optimal solution runs in O(n) time because the string is scanned at most twice. Each pass performs constant-time operations per character. Space complexity is O(1) when using the greedy counter technique, or O(n) if storing indices using a hash or stack structure.

Comments

Now write it yourselfClose this page and solve Check if a Parentheses String Can Be Valid in the editor, without looking.
Practise Check if a Parentheses String Can Be Valid