Notes on how to solve the Valid Parentheses problem
Table of Contents
This series of post is like my coding diary – a place where I jot down what worked (and what didn’t) so that future-me can have an easier go at it. In this post I’ll take a look at the Valid Parentheses problem
Problem statement
Given a string s containing just the characters (
, )
, {
, }
, [
and ]
, determine if the input string is valid.
An input string is valid if:
- Open brackets must be closed by the same type of brackets.
- Open brackets must be closed in the correct order.
- Every close bracket has a corresponding open bracket of the same type.
Examples
Example 1:
Input: s = "()"
→ Output: true
Example 2:
Input: s = "()[]{}"
→ Output: true
Example 3:
Input: s = "(]"
→ Output: false
My thought process
The first thing that comes to mind is to have some sort of memory of the open brackets that I’ve seen so far. If I find a closing bracket, I can check if the last open bracket is of the same type. If it is, I can remove it from the memory and continue. If it isn’t, I can return false
.
The best structure for this is of course a stack. We are going to add to the stack every time we find an opening bracket and remove from it every time we find a closing bracket. If the stack is empty when we find a closing bracket, we can return false
. If the stack has elements when we finish iterating through the string, we can return false
. Otherwise life is good and we return true
.
I’ll use a new example for this:
s = "{[()]}"
i = 0
↓
"{[()]}"
Since { it's an opening bracket
we add it to the stack → stack = ["{"]
i = 1
↓
"{[()]}"
Since [ it's an opening bracket
we add it to the stack → stack = ["{", "["]
i = 2
↓
"{[()]}"
Since ( it's an opening bracket
we add it to the stack → stack = ["{", "[", "("]
i = 3
↓
"{[()]}"
Since ) it's a closing bracket
we check if the last element of the stack is a matching bracket,
it is, so we pop it out → stack = ["{", "["]
i = 4
↓
"{[()]}"
Since ] it's a closing bracket
we check if the last element of the stack is a matching bracket,
it is, so we pop it out → stack = ["{"]
i = 5
↓
"{[()]}"
Since } it's a closing bracket
we check if the last element of the stack is a matching bracket,
it is, so we pop it out → stack = []
We finshed iterating through the string and the stack is empty, so we return true.
My solution
class Solution:
# Using a stack
def isValid(self, s: str) -> bool:
stack = []
matching_brackets = {
")": "(",
"]": "[",
"}": "{"
}
for char in s:
if char in matching_brackets.values(): # It's an opening bracket
stack.append(char)
else: # It's a closing bracket
if len(stack) == 0:
# if we don't have any opening brackets in the stack
return False
if stack.pop() != matching_brackets[char]:
# if the last opening bracket is not of the same type
return False
return True
Possible follow-up questions
Can we do it by using a O(1) memory?
If we don’t use a stack, we can use a counter. We can increment the counter every time we find an opening bracket and decrement it every time we find a closing bracket. If the counter is negative at any point, we can return false
. If the counter is not zero when we finish iterating through the string, we can return false
. Otherwise life is good and we return true
.
Since we have three type of parentheses, we need three counters.
class Solution:
def isValid(self, s: str) -> bool:
open_parentheses = 0
open_brackets = 0
open_braces = 0
for char in s:
if char == "(":
open_parentheses += 1
elif char == "[":
open_brackets += 1
elif char == "{":
open_braces += 1
elif char == ")":
open_parentheses -= 1
elif char == "]":
open_brackets -= 1
elif char == "}":
open_braces -= 1
if open_parentheses < 0 or open_brackets < 0 or open_braces < 0:
return False
return open_parentheses == 0 and open_brackets == 0 and open_braces == 0
This solution is not as elegant as the previous one, but it’s still O(n)
time complexity and O(1)
memory complexity.