20 · String Parsing & Expression Evaluation
#224 Basic Calculator
frequently reported atUberAmazonMicrosoftGoogleMetaBloomberg
the problem
In plain words
s is a valid expression with non-negative integers, '+', '-', brackets and spaces. A '-' can also mean "negative" at the very start or right after '(' — for example "-(2 + 3)" — but '+' is never used that way. Evaluate it without a built-in evaluator. Every number and every running total fits in a 32-bit int.
in s = "1 + 1"
out 2
in s = " 2-1 + 2 "
out 3
in s = "(1+(4+5+2)-3)+(6+8)"
out 23
(1 + 11 − 3) + 14 = 9 + 14.
- 1 ≤ s.length ≤ 3·10⁵
- s has digits, '+', '-', '(', ')' and ' '
- s is valid; two operators never touch
- Every number and running total fits in an int
your turn
Try it before you peek
Read the examples, say the brute force out loud, then write code: here or on LeetCode ↗. Stuck for 20 minutes? Open one hint at a time.
Solve it in the browser
LeetCode-style: complete the class. Compiled by the real javac (Java 17) and judged right here — nothing is sent anywhere.
Solutions are hidden so you can try first. Even 20 minutes of real effort makes the answer stick far better than reading it.
Marking the problem solved, or opening every hint, also shows it.
the aha
Key insight
patternStack for matching & nestingString parsing
where this runsjavac parses binary operators with an operand stack and an operator stack inside its otherwise recursive-descent parser.
With only + and −, a bracket just keeps or flips the signs inside it. At '(' save the total so far and the sign in front; at ')' glue the group back on: outside + sign × inside.
solutions
From brute force to optimal
The first ')' closes an innermost group. Evaluate that flat group, write its value into the string, and start again.
- Remove the spaces.
- While there is a ')': find it and the last '(' before it; evaluate the inside (only + and −); splice the value in.
- Evaluate what is left. A run of signs like "--" (from "1-(-2)") folds into one sign.
Up to about n/2 groups, and each rewrite rebuilds the whole string.
The rewritten strings.
class Solution { public int calculate(String s) { String e = s.replace(" ", ""); int close; while ((close = e.indexOf(')')) >= 0) { // the first ')' closes an innermost group int open = e.lastIndexOf('(', close); long inner = evalFlat(e.substring(open + 1, close)); e = e.substring(0, open) + inner + e.substring(close + 1); // rebuild the whole string } return (int) evalFlat(e); } /** Only numbers, + and -. A run of signs like "--" (from "1-(-2)" → "1--2") folds into one. */ private long evalFlat(String e) { long total = 0; int i = 0; while (i < e.length()) { int sign = 1; while (e.charAt(i) == '+' || e.charAt(i) == '-') { if (e.charAt(i) == '-') sign = -sign; i++; } long num = 0; while (i < e.length() && e.charAt(i) >= '0' && e.charAt(i) <= '9') num = num * 10 + (e.charAt(i++) - '0'); total += sign * num; } return total; }}Keep result, sign and num for the current level. '(' pushes the outside's result and the sign before the bracket; ')' pops them and combines.
- Digit → num = num × 10 + d.
- '+' or '-' → result += sign × num; num = 0; sign = ±1.
- '(' → push result, push sign; result = 0; sign = 1.
- ')' → result += sign × num; num = 0; groupSign = pop(); outside = pop(); result = outside + groupSign × result.
- Return result + sign × num.
Each character is handled once; each bracket pushes and pops one pair.
One pair per open bracket — O(n) in the worst case, on the heap.
class Solution { public int calculate(String s) { Deque<Integer> stack = new ArrayDeque<>(); // saved (result, sign) for every open '(' int result = 0, sign = 1, num = 0; for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c >= '0' && c <= '9') { num = num * 10 + (c - '0'); } else if (c == '+' || c == '-') { result += sign * num; // the number before this operator is complete num = 0; sign = c == '+' ? 1 : -1; } else if (c == '(') { stack.push(result); // save the outside: its total so far ... stack.push(sign); // ... and the sign in front of this group result = 0; sign = 1; } else if (c == ')') { result += sign * num; // finish the inside num = 0; int groupSign = stack.pop(); int outside = stack.pop(); result = outside + groupSign * result; // glue the group onto the outside } } return result + sign * num; }}Dry run
A shared cursor; group() reads until its ')' and returns the value. '(' calls group() — the call stack saves result and sign.
- Digits → a number added with the current sign.
- '+' / '-' → set sign.
- '(' → result += sign × group().
- ')' → return result.
The cursor only moves forward, so every character is read once.
One stack frame per open bracket; very deep nesting can throw StackOverflowError.
class Solution { private String s; private int i; // the cursor, shared by every call public int calculate(String s) { this.s = s; this.i = 0; return group(); } /** Reads up to the matching ')' (or the end) and returns the value of that group. */ private int group() { int result = 0, sign = 1; while (i < s.length()) { char c = s.charAt(i++); if (c >= '0' && c <= '9') { int num = c - '0'; while (i < s.length() && s.charAt(i) >= '0' && s.charAt(i) <= '9') num = num * 10 + (s.charAt(i++) - '0'); result += sign * num; } else if (c == '+') { sign = 1; } else if (c == '-') { sign = -1; } else if (c == '(') { result += sign * group(); // the call stack saves result and sign for us } else if (c == ')') { return result; } } return result; }}in the room
How to say it in the interview
talk track
- Clarify: only + and −? Where can unary minus appear? Spaces anywhere? (Yes; at the start and after '('; yes.)
- Name the state: result, sign and num for the current level.
- At '(' push result and sign, then reset; at ')' pop both and combine.
- Offer the recursive version and mention its depth risk on deeply nested input.
traps
- Forgetting the last number after the loop: return result + sign × num.
- Popping in the wrong order: the sign was pushed last, so it comes off first.
- Treating "-(" as an error: a unary minus is just sign = −1 with nothing to add before it.
what they ask next
Follow-ups
Add * and / (Basic Calculator III, LC 772, Premium)?
Use the two-stack shunting-yard from this chapter, or recursive descent: expr → term → factor, where factor reads a number or a bracketed expr.
What if the nesting is thousands of levels deep?
The sign-stack version keeps its pairs in an ArrayDeque on the heap, so it is safe. A recursive solution needs one call per level and can overflow the thread's stack.
before you submit
Edge cases to test
- Unary minus at the start and after '(': "-(2+3)" → −5, "1-(-2)" → 3.
- Brackets around one number: "(((7)))" → 7.
- Deep nesting of hundreds of levels: safe for the stack version.
practise next
Similar problems
- #227Basic Calculator IIMediumin Parsing
- #394Decode StringMediumin Stacks
- #20Valid ParenthesesEasyin Stacks