Good day, everybody!
I've added the problem to online contest
http://acm.mipt.ru/judge.
http://acm.mipt.ru/judge/problems.pl?problem=126&lang=en
Now you can check yourself!
Sorry, I will use 'gets' instead of 'ARGV'.
#########################################
# OK
# Let's start with simple one.
# This one just does the job without removing odd parentheses
stack = []
gets.strip.split.each do |token|
case token
when '*', '+', '/', '-'
stack << [')', stack.pop, token, stack.pop, '('].reverse!
else
stack << token
end
end
puts stack.flatten.join
########################################
# Now let's do the thing we are here for.
# We will use idea of operator strength.
# Each operator has left and right strength.
# Binary operation should "protect" itself with parentheses if there
is stronger operator
# to the left or to the right. Two neighbor operators affect each
other with strengths:
# one with left-strength (the one to the right) and another with right-strength
# (the one to the left)
#
OP_STRENGTH = {
:left => {'+'=>2, '-'=>2, '*'=>4, '/'=>4},
:right => {'+'=>2, '-'=>3, '*'=>4, '/'=>5}
}
stack = []
gets.strip.split.each do |token|
# puts "TOKEN '#{token.inspect}'"
case token
when '*', '+', '/', '-'
stack << [stack.pop, token, stack.pop].reverse!
else
stack << token
end
end
# Uncomment these line to see some sort of 'parse tree'
# require 'yaml'
# puts stack.to_yaml
def parenthesize(triplet, top_op_strength, side)
if triplet.is_a? Array
parenthesize(triplet[0], OP_STRENGTH[:left][triplet[1]], :right)
parenthesize(triplet[2], OP_STRENGTH[:right][triplet[1]], :left)
if OP_STRENGTH[side][triplet[1]] < top_op_strength
triplet.push ')'
triplet.unshift '('
end
end
end
parenthesize(stack.last, 0, :right)
puts stack.flatten.join
#########################################
#
#
# Lets try the previous version with input
# '0 ' + (1..N-1).to_a.join(' - ') + ' -',
# for N = 15000, 30000, 60000
# We will see two thins
# 1) in `parenthesize': stack level too deep (SystemStackError)
# 2) time grows quadratically. But why? The bad guy is 'flatten'!
# First of all we should get rid of recursion:
def parenthesize(triplet, top_op_strength, side)
return unless triplet.is_a?(Array)
q = [ [triplet, top_op_strength, side] ]
while !q.empty?
t,top_op_strength,side = q.pop
q << [t[0], OP_STRENGTH[:left][t[1]], :right] if t[0].is_a?(Array)
q << [t[2], OP_STRENGTH[:right][t[1]], :left] if t[2].is_a?(Array)
if OP_STRENGTH[side][t[1]] < top_op_strength
t.push ')'
t.unshift '('
end
end
end
#########################################
#
# The previous version still work O( L^2), where L is number of
tokens in input expression.
# Let's get rid of 'flatten':
#
def parenthesize(triplet, top_op_strength, side)
q = [ [triplet, top_op_strength, side] ]
while !q.empty?
t,top_op_strength,side = q.pop
if t.is_a?(Array)
if OP_STRENGTH[side][t[1]] < top_op_strength
print '('
q << ')'
end
q << [t[2], OP_STRENGTH[:right][t[1]], :left]
q << t[1]
q << [t[0], OP_STRENGTH[:left][t[1]], :right]
else
print t
end
end
end
parenthesize(stack.last, 0, :right)
puts
############################################
#
# And finally, one may prefer Hash version of parse-tree (though
it's a little bit slower):
OP_STRENGTH = {
:left => {'+'=>2, '-'=>2, '*'=>4, '/'=>4},
:right => {'+'=>2, '-'=>3, '*'=>4, '/'=>5}
}
stack = []
gets.strip.split.each do |token|
case token
when '*', '+', '/', '-'
stack << {:r=>stack.pop,

p=>token, :l=>stack.pop}
else
stack << token
end
end
def parenthesize(triplet, top_op_strength, side)
q = [ [triplet, top_op_strength, side] ]
while !q.empty?
t,top_op_strength,side = q.pop
if t.is_a?(Hash)
if OP_STRENGTH[side][t[

p]] < top_op_strength
print '('
q << ')'
end
q << [t[:r], OP_STRENGTH[:right][t[

p]], :left]
q << t[

p]
q << [t[:l], OP_STRENGTH[:left][t[

p]], :right]
else
print t
end
end
end
parenthesize(stack.last, 0, :right)
puts
############################################
Final remarks.
1. Defining left -and right- operator strengths allows us to take into
account different aspects
1) priority
2) commutativity/ non commutativity
3) associativity type (left or right)
2. We discovered problem with Array#flatten. Is it a known issue?
(I use ruby 1.8.6 (2007-03-13 patchlevel 0) [i386-mswin32])
For N = 10000, 20000 and input = '0 ' + (1..N-1).to_a.join(' - ') + ' -'
require 'benchmark'
puts Benchmark.measure {
stack.flatten
}
return the following results:
N time
5000 1.265000
10000 5.141000
20000 20.484000
So, it's quadratic.
While final solution works less than 1 second (0.079 seconds).
What's the problem with flatten?