- Leetcode 224
- Difficulty: Hard
- Patterns:
Problem Definition
Given a string representing a valid math expression, evaluate the expression and return its value.
The expression contains the characters [0-9]+-()
and does not contain multiplication nor division of any sort.
Assume that the input is valid and does not contain any other characters.
Examples
Some test cases to verify our solution.
assert evaluate("1+2+3") == 6
assert evaluate("-(2 + 3)") == -5
assert evaluate("-(3 + 5 + (1 + 2 - (4 - 4)))") == -11
assert evaluate("(3 + 4)") == 7
assert evaluate(" ( 3 ) ") == 3
assert evaluate("1-1") == 0
Solution
The string will represent a sequence of items to add, with those items possibly being negative.
For 1+2+3
, the items are 1
, 2
, and 3
.
For -(2 + 3)
, the items are -x
, with x
being its own sum consisting of the items
2
and 3
.
For s = -(3 + 5 + (1 + 2 - (4 - 4)))
, we have:
As this breakdown suggests, we can use a stack to store the state of the
outer calculation while we calculate the inner value - we can store the state of the calculation of s
while we calculate the value for x
; we can store the state of the calculation of x
while we calculate the value for y
; etc.
We'll implement our algorithm as follows:
- We will create an
res
variable to contain the current running sum. - We will initialize a
num
with value0
to capture newly encountered numbers. - When we encounter a number, we will multiply the existing
num
by 10 and add the new number to it, thereby constructing our number as we go along. - When we encounter a
-
, we will note that the next value to be added will be negative. - When we encounter a
(
, we will push the current state of the current running sum onto a stack in the form of a(res, negative) tuple. - Inside the
(
, we will start a newres
and calculate the sum. - Once we hit a
)
, we'll pop a frame off our stack and add the current innerres
to the outerres
, with it possibly negated based on the popped stack frame'snegative
value. - We will simply ignore all spaces.
- Whenever we hit a non-number, other than a space, we will add the existing
num
to theres
.
def evaluate(s: str) -> int:
res = 0
negative = False
stack = []
num = 0
for c in s:
if c == "-":
res += -num if negative else num
num = 0
negative = True
elif c == "+":
res += -num if negative else num
num = 0
negative = False
elif c == "(":
res += -num if negative else num
num = 0
stack.append((res, negative))
res = 0
negative = False
elif c == ")":
res += -num if negative else num
num = 0
prev_res, prev_negative = stack.pop()
res = prev_res + (-res if prev_negative else res)
elif c != " ":
num = num * 10 + int(c)
return res + (-num if negative else num)
Complexity
Let $n =$ len(s)
.
Runtime complexity should be $\Theta(n)$ since we are traversing the string fully once.
Auxiliary space complexity should be $O(n)$ since we might create roughly $\frac{n}{2} - 1$ elements in our stack.
For example, the calculation of something like (((((1)))))
would generate 5 stack items.