LeetCode 3522 - Calculate Score After Performing Instructions
This problem asks you to simulate a linear sequence of instructions that can either modify a running score or move the instruction pointer by a specified offset. You are given two arrays, instructions and values, both of size n. Each index i represents an instruction.
Difficulty: 🟡 Medium
Topics: Array, Hash Table, String, Simulation
Solution
Problem Understanding
This problem asks you to simulate a linear sequence of instructions that can either modify a running score or move the instruction pointer by a specified offset. You are given two arrays, instructions and values, both of size n. Each index i represents an instruction. If instructions[i] is "add", you add values[i] to a running score and advance to the next instruction. If instructions[i] is "jump", you move the instruction pointer by values[i] without changing the score.
The simulation stops if you either move out of bounds or attempt to execute an instruction you have already executed. The problem requires returning the total score accumulated when the process stops.
Constraints indicate that n can be as large as 10^5 and values can range from -10^5 to 10^5. This implies the solution must handle large input efficiently, and naive repeated traversal could be too slow if not careful.
Important edge cases include:
- A jump of 0, which could immediately lead to revisiting the same instruction.
- Jumps that move out of bounds immediately.
- All instructions being
"add"or all"jump". - Negative values causing backward jumps.
Approaches
Brute Force Approach
The simplest approach is a direct simulation. Start at index 0, maintain a visited set to track executed instructions, and iterate according to the rules. For each instruction, check whether the next index would be out of bounds or previously visited. If not, perform the operation (add or jump) and continue.
This approach is correct but has a time complexity of O(n) in the best case, but O(n) worst-case is acceptable given n <= 10^5. Using a set for visited indices ensures O(1) lookup, so this naive simulation is actually efficient enough for the constraints.
Optimal Approach
Since the brute-force simulation is already O(n) and each instruction is executed at most once, it is effectively optimal. There are no hidden optimizations needed because revisiting instructions ends the simulation, and each instruction is processed exactly once.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n) | O(n) | Direct simulation with a set to track visited instructions |
| Optimal | O(n) | O(n) | Same as brute force; each instruction executed at most once |
Algorithm Walkthrough
- Initialize
scoreto 0 andindexto 0 to track the current instruction. - Create a set
visitedto store indices that have already been executed. - Loop while
indexis within bounds (0 <= index < n) and not invisited. - Add the current
indexto thevisitedset. - Check the instruction type:
- If
"add", incrementscorebyvalues[index]and move toindex + 1. - If
"jump", move toindex + values[index]without changingscore.
- Repeat until the loop terminates because the index is out of bounds or revisited.
- Return the final
score.
Why it works: Each instruction is executed at most once, and the simulation follows the exact rules. The use of a visited set ensures that revisits immediately terminate the process. All edge cases like jumps out of bounds or zero jumps are handled naturally by the loop condition.
Python Solution
from typing import List
class Solution:
def calculateScore(self, instructions: List[str], values: List[int]) -> int:
score = 0
index = 0
visited = set()
n = len(instructions)
while 0 <= index < n and index not in visited:
visited.add(index)
if instructions[index] == "add":
score += values[index]
index += 1
else: # "jump"
index += values[index]
return score
Explanation: The while loop ensures that the simulation stops correctly. The visited set prevents revisiting instructions. "add" instructions update the score and move sequentially, while "jump" updates the index without changing the score. This implements the algorithm directly and efficiently.
Go Solution
func calculateScore(instructions []string, values []int) int64 {
var score int64 = 0
index := 0
visited := make(map[int]bool)
n := len(instructions)
for index >= 0 && index < n && !visited[index] {
visited[index] = true
if instructions[index] == "add" {
score += int64(values[index])
index++
} else { // "jump"
index += values[index]
}
}
return score
}
Explanation: Go requires using int64 to handle large values for score. The visited map tracks visited indices, similar to the Python set. The logic mirrors the Python solution, with proper handling of types and map access.
Worked Examples
Example 1
instructions = ["jump","add","add","jump","add","jump"], values = [2,1,3,1,-2,-3]
| Step | Index | Instruction | Value | Score | Next Index | Visited |
|---|---|---|---|---|---|---|
| 1 | 0 | jump | 2 | 0 | 2 | {0} |
| 2 | 2 | add | 3 | 3 | 3 | {0,2} |
| 3 | 3 | jump | 1 | 3 | 4 | {0,2,3} |
| 4 | 4 | add | -2 | 1 | 5 | {0,2,3,4} |
| 5 | 5 | jump | -3 | 1 | 2 | {0,2,3,4,5} |
Index 2 already visited, stop. Final score = 1
Example 2
instructions = ["jump","add","add"], values = [3,1,1]
| Step | Index | Instruction | Value | Score | Next Index | Visited |
|---|---|---|---|---|---|---|
| 1 | 0 | jump | 3 | 0 | 3 | {0} |
Index 3 out of bounds, stop. Final score = 0
Example 3
instructions = ["jump"], values = [0]
| Step | Index | Instruction | Value | Score | Next Index | Visited |
|---|---|---|---|---|---|---|
| 1 | 0 | jump | 0 | 0 | 0 | {0} |
Index 0 already visited, stop. Final score = 0
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) | Each instruction is processed at most once and set lookups are O(1) |
| Space | O(n) | The visited set stores up to n indices |
The algorithm is efficient given the constraints, and both time and space scale linearly with input size.
Test Cases
# Basic examples
assert Solution().calculateScore(["jump","add","add","jump","add","jump"], [2,1,3,1,-2,-3]) == 1
assert Solution().calculateScore(["jump","add","add"], [3,1,1]) == 0
assert Solution().calculateScore(["jump"], [0]) == 0
# All add instructions
assert Solution().calculateScore(["add","add","add"], [1,2,3]) == 6
# All jump instructions
assert Solution().calculateScore(["jump","jump","jump"], [1,2,3]) == 0
# Jump out of bounds
assert Solution().calculateScore(["jump","add"], [5,10]) == 0
# Negative jumps
assert Solution().calculateScore(["add","jump","add"], [1,-1,2]) == 1
# Large input
instructions = ["add"]*100000
values = [1]*100000
assert Solution().calculateScore(instructions, values) == 100000
| Test | Why |
|---|---|
| Provided examples | Validates normal operation and stopping conditions |
| All add | Ensures sequential adds accumulate correctly |
| All jump | Ensures jumps alone terminate simulation if out of bounds |
| Jump out of bounds | Edge case for immediate termination |
| Negative jumps | Tests backward movement handling |
| Large input | Stress test for performance |
Edge Cases
Zero Jump: A jump of value 0 immediately revisits the same instruction. Without the visited set, this could cause an infinite loop. Using visited ensures proper termination.
Negative Jump: Instructions can move backward in the array. The algorithm correctly updates index and checks for revisits to avoid infinite loops.
Out-of-Bounds Jump: Any jump moving beyond 0 <= index < n terminates the simulation. This includes both forward jumps past n-1 and backward jumps below 0. The while loop condition guarantees termination.