title: Baseball Game slug: baseball-game difficulty: easy leetcode_id: 682 leetcode_url: https://leetcode.com/problems/baseball-game/ categories: - arrays - stack patterns: - monotonic-stack description: | You are keeping the scores for a baseball game with strange rules. At the beginning of the game, you start with an empty record. You are given a list of strings `operations`, where `operations[i]` is the ith operation you must apply to the record and is one of the following: - An integer `x` — Record a new score of `x`. - `'+'` — Record a new score that is the sum of the previous two scores. - `'D'` — Record a new score that is the double of the previous score. - `'C'` — Invalidate the previous score, removing it from the record. Return *the sum of all the scores on the record after applying all the operations*. The test cases are generated such that the answer and all intermediate calculations fit in a **32-bit** integer and that all operations are valid. constraints: | - `1 <= operations.length <= 1000` - `operations[i]` is `"C"`, `"D"`, `"+"`, or a string representing an integer in the range `[-3 * 10^4, 3 * 10^4]` - For operation `"+"`, there will always be at least two previous scores on the record - For operations `"C"` and `"D"`, there will always be at least one previous score on the record examples: - input: 'ops = ["5","2","C","D","+"]' output: "30" explanation: | "5" - Add 5 to the record, record is now [5]. "2" - Add 2 to the record, record is now [5, 2]. "C" - Invalidate and remove the previous score, record is now [5]. "D" - Add 2 * 5 = 10 to the record, record is now [5, 10]. "+" - Add 5 + 10 = 15 to the record, record is now [5, 10, 15]. The total sum is 5 + 10 + 15 = 30. - input: 'ops = ["5","-2","4","C","D","9","+","+"]' output: "27" explanation: | "5" - Add 5 to the record, record is now [5]. "-2" - Add -2 to the record, record is now [5, -2]. "4" - Add 4 to the record, record is now [5, -2, 4]. "C" - Invalidate and remove the previous score, record is now [5, -2]. "D" - Add 2 * -2 = -4 to the record, record is now [5, -2, -4]. "9" - Add 9 to the record, record is now [5, -2, -4, 9]. "+" - Add -4 + 9 = 5 to the record, record is now [5, -2, -4, 9, 5]. "+" - Add 9 + 5 = 14 to the record, record is now [5, -2, -4, 9, 5, 14]. The total sum is 5 + -2 + -4 + 9 + 5 + 14 = 27. - input: 'ops = ["1","C"]' output: "0" explanation: | "1" - Add 1 to the record, record is now [1]. "C" - Invalidate and remove the previous score, record is now []. Since the record is empty, the total sum is 0. explanation: intuition: | Imagine you're keeping score on a notepad where you can only add entries at the bottom and erase the most recent entry. This is exactly how a **stack** works — Last In, First Out (LIFO). The key insight is that every operation either: - **Adds** a new score to the end of the record (integer, `+`, `D`) - **Removes** the most recent score (`C`) This matches perfectly with stack operations: `push` to add and `pop` to remove. The stack always maintains the current valid scores in order, so we can easily access the last one or two elements when needed. Think of it like this: the record is a stack of paper slips. When you get a new score, you place a slip on top. When you need to double (`D`) or sum (`+`), you peek at the top slip(s), calculate, and add a new slip. When you invalidate (`C`), you simply remove the top slip. approach: | We solve this using a **Stack Simulation** approach: **Step 1: Initialise an empty stack** - `record`: An empty list to act as our stack, storing valid scores   **Step 2: Process each operation** - For each operation in the input: - If it's an **integer**: Convert to int and push onto the stack - If it's `"+"`: Add the sum of the last two scores (`record[-1] + record[-2]`) to the stack - If it's `"D"`: Add double the last score (`2 * record[-1]`) to the stack - If it's `"C"`: Pop the last score from the stack   **Step 3: Return the sum** - Return `sum(record)` — the sum of all remaining valid scores   The stack naturally maintains only the valid scores at any point. Since the problem guarantees all operations are valid (e.g., `+` always has at least two previous scores), we don't need additional boundary checks. common_pitfalls: - title: Not Recognising the Stack Pattern description: | Some developers try to use complex conditionals or maintain separate variables instead of recognising this as a classic stack problem. The operations map directly to stack operations: - Integer → `push(value)` - `"+"` → `push(peek(-1) + peek(-2))` - `"D"` → `push(2 * peek(-1))` - `"C"` → `pop()` Once you see this mapping, the solution becomes straightforward. wrong_approach: "Complex state tracking with multiple variables" correct_approach: "Use a stack to track the record" - title: Integer Parsing Edge Cases description: | Remember that scores can be **negative** (e.g., `"-2"`). When checking if an operation is an integer, don't just check if the first character is a digit — it could be a minus sign. In Python, using `try/except` with `int()` or checking with `lstrip('-').isdigit()` handles this correctly. Alternatively, check if the operation is not one of the three special characters. wrong_approach: "Checking only if first char is digit" correct_approach: "Check if operation is not in {'C', 'D', '+'}" - title: Off-by-One in Index Access description: | When accessing the last two elements for the `"+"` operation, remember: - `record[-1]` is the most recent (last) score - `record[-2]` is the second-to-last score The problem guarantees at least two scores exist for `"+"`, so this access is always safe. key_takeaways: - "**Stack pattern recognition**: When operations involve 'most recent' or 'undo', think stack" - "**Simulation problems**: Walk through the operations step-by-step, maintaining state as you go" - "**Python negative indexing**: `list[-1]` and `list[-2]` provide clean access to recent elements" - "**Trust the constraints**: When the problem guarantees valid operations, you can skip defensive checks" time_complexity: "O(n). We process each operation exactly once, where `n` is the length of `operations`." space_complexity: "O(n). In the worst case, all operations are integers, so the stack stores `n` elements." solutions: - approach_name: Stack Simulation is_optimal: true code: | def cal_points(operations: list[str]) -> int: # Stack to track valid scores record = [] for op in operations: if op == '+': # Sum of the last two scores record.append(record[-1] + record[-2]) elif op == 'D': # Double the last score record.append(2 * record[-1]) elif op == 'C': # Remove the last score record.pop() else: # It's an integer score record.append(int(op)) # Return sum of all valid scores return sum(record) explanation: | **Time Complexity:** O(n) — Single pass through operations, each operation is O(1). **Space Complexity:** O(n) — Stack can grow to size n in the worst case. We simulate the game by processing each operation and maintaining a stack of valid scores. The stack's LIFO property perfectly matches the problem's requirement to access and modify the most recent scores. - approach_name: Stack with Running Sum is_optimal: false code: | def cal_points(operations: list[str]) -> int: record = [] total = 0 for op in operations: if op == '+': score = record[-1] + record[-2] elif op == 'D': score = 2 * record[-1] elif op == 'C': # Subtract the removed score from total total -= record.pop() continue else: score = int(op) record.append(score) total += score return total explanation: | **Time Complexity:** O(n) — Single pass through operations. **Space Complexity:** O(n) — Same stack storage. This variation maintains a running total, adding scores as they're pushed and subtracting when popped. While it avoids calling `sum()` at the end, the overall complexity is the same. The first approach is cleaner and preferred.