One way to do that is to successively remove strings '()'
, '[]'
and '{}'
until either the string is empty, in which case the string is found to be balanced, or at some iteration no string '()'
, '[]'
or '{}'
is found, in which case the string is found to be unbalanced.
RGX = /\(\)|\[\]|{}/
def valid_braces(str)
s = str.dup
until s.empty?
return false if s.gsub!(RGX, '').nil?
end
true
end
valid_braces '(){}[]' #=> true
valid_braces '([{}])' #=> true
valid_braces '([[{[]}]{}]())' #=> true
valid_braces '(}' #=> false
valid_braces '[(])' #=> false
valid_braces '[({})](]' #=> false
valid_braces('[A]') #=> false
The regular expression reads, "match '()' or '[]' or '{}'". Note that String#gsub! returns nil
if no substitutions are performed.
I can insert a puts
statement to illustrate what is happening at each iteration.
def valid_braces(str)
s = str.dup
until s.empty?
puts s
return false if s.gsub!(RGX, '').nil?
end
true
end
valid_braces '([[{[]}]{}]())'
([[{[]}]{}]()) full string
([[{}]]) '[]', '{}' and '()' removed
([[]]) '{}' removed
([]) '[]' removed
() '[]' removed
'()' removed
#=> true as `str` is empty
This is probably not as efficient as using a stack, as @spickermann has done. I would implement that a little differently.
CLOSE_TO_OPEN = { ')'=>'(', ']'=>'[', '}'=>'{' }
OPENS = CLOSE_TO_OPEN.values
#=> ["(", "[", "{"]
def valid_braces(str)
stack = []
str.each_char do |c|
if OPENS.include?(c)
stack << c
else
return false unless CLOSE_TO_OPEN[c] == stack.pop
end
end
return stack.empty?
end
valid_braces '(){}[]' #=> true
valid_braces '([{}])' #=> true
valid_braces '([[{[]}]{}]())' #=> true
valid_braces '(}' #=> false
valid_braces '[(])' #=> false
valid_braces '[({})](]' #=> false
valid_braces('[A]') #=> false
We can add puts
statements to see what is happening.
def valid_braces(str)
stack = []
str.each_char do |c|
print "stack = #{stack}, c = '#{c}', "
if OPENS.include?(c)
stack << c
puts "pushed '#{c}'"
else
return false unless CLOSE_TO_OPEN[c] == stack.pop
puts "popped '#{CLOSE_TO_OPEN[c]}'"
end
end
puts "stack at end = #{stack}"
return stack.empty?
end
valid_braces '([[{[]}]{}]())'
stack = [], c = '(', pushed '('
stack = ["("], c = '[', pushed '['
stack = ["(", "["], c = '[', pushed '['
stack = ["(", "[", "["], c = '{', pushed '{'
stack = ["(", "[", "[", "{"], c = '[', pushed '['
stack = ["(", "[", "[", "{", "["], c = ']', popped '['
stack = ["(", "[", "[", "{"], c = '}', popped '{'
stack = ["(", "[", "["], c = ']', popped '['
stack = ["(", "["], c = '{', pushed '{'
stack = ["(", "[", "{"], c = '}', popped '{'
stack = ["(", "["], c = ']', popped '['
stack = ["("], c = '(', pushed '('
stack = ["(", "("], c = ')', popped '('
stack = ["("], c = ')', popped '('
stack at end = []
#=> true