Implement Spreadsheet Cell Updates
Company: Harvey
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: hard
Interview Round: Technical Screen
Quick Answer: This question evaluates skills in data structures, expression parsing, dependency graph management, incremental evaluation, and cycle detection for maintaining consistent spreadsheet state.
Part 1: Integer-Only Spreadsheet Cells
Constraints
- 0 <= len(operations) <= 100000
- Each cell name is a non-empty string such as A1 or AA15
- Integer values fit in 32-bit signed range
- Undefined cells should be treated as 0
Examples
Input: [('SET', 'A1', 10), ('GET', 'A1'), ('SET', 'B2', -3), ('GET', 'B2'), ('GET', 'C1')]
Expected Output: [10, -3, 0]
Explanation: Reads back stored values, and C1 is undefined so it returns 0.
Input: [('SET', 'AA12', 7), ('GET', 'AA12')]
Expected Output: [7]
Explanation: Cell names may have multiple letters.
Input: [('SET', 'A1', 5), ('SET', 'A1', 8), ('GET', 'A1')]
Expected Output: [8]
Explanation: A later SET overwrites the previous value.
Input: []
Expected Output: []
Explanation: Edge case with no operations.
Hints
- A hash map keyed by cell name is enough for this version.
- You do not need to parse the row or column part of the cell name.
Part 2: Spreadsheet Formulas with Immediate Evaluation
Constraints
- 0 <= len(operations) <= 100000
- Formulas are valid and begin with '='
- A formula uses only '+' between terms
- Each term is either an integer literal or a cell name
- Undefined referenced cells should be treated as 0
Examples
Input: [('SET', 'B1', 10), ('SET', 'A1', '=B1+5'), ('GET', 'A1')]
Expected Output: [15]
Explanation: A1 is evaluated immediately as 10 + 5.
Input: [('SET', 'A1', '=2+3+4'), ('GET', 'A1')]
Expected Output: [9]
Explanation: A formula may contain only integer literals.
Input: [('SET', 'A1', '=B1+7'), ('GET', 'A1')]
Expected Output: [7]
Explanation: B1 is undefined, so it contributes 0.
Input: [('SET', 'A1', 3), ('SET', 'B1', '=A1+2'), ('SET', 'C1', '=B1+4'), ('GET', 'C1')]
Expected Output: [9]
Explanation: B1 becomes 5, then C1 becomes 9.
Input: []
Expected Output: []
Explanation: Edge case with no operations.
Hints
- Remove the leading '=' and split the expression on '+'.
- For each term, decide whether it is an integer literal or a cell name.
Part 3: Live Formulas with Dependency Updates
Constraints
- 0 <= len(operations) <= 50000
- Formulas are valid and begin with '='
- A formula uses only '+' between terms
- Each term is either an integer literal or a cell name
- Undefined referenced cells should be treated as 0
- No circular dependency will appear in this part
Examples
Input: [('SET', 'B1', 10), ('SET', 'A1', '=B1+5'), ('GET', 'A1'), ('SET', 'B1', 20), ('GET', 'A1')]
Expected Output: [15, 25]
Explanation: Updating B1 should also update A1.
Input: [('SET', 'A1', 1), ('SET', 'B1', '=A1+2'), ('SET', 'C1', '=B1+3'), ('GET', 'C1'), ('SET', 'A1', 10), ('GET', 'C1')]
Expected Output: [6, 15]
Explanation: The update to A1 propagates through B1 into C1.
Input: [('SET', 'X1', 1), ('SET', 'B1', '=X1+1'), ('SET', 'C1', '=X1+2'), ('SET', 'A1', '=B1+C1'), ('GET', 'A1'), ('SET', 'X1', 5), ('GET', 'A1')]
Expected Output: [5, 13]
Explanation: A1 depends on two cells that both depend on X1.
Input: [('SET', 'A1', '=B1+5'), ('GET', 'A1'), ('SET', 'B1', 10), ('GET', 'A1')]
Expected Output: [5, 15]
Explanation: Undefined references start at 0 and later updates should propagate.
Input: [('GET', 'Z9')]
Expected Output: [0]
Explanation: Edge case: reading an undefined cell returns 0.
Hints
- Track both forward dependencies and reverse dependencies.
- When one cell changes, only recompute cells reachable through the reverse-dependency graph.
Part 4: Circular Dependency Detection in a Spreadsheet
Constraints
- 0 <= len(operations) <= 50000
- Formulas are valid and begin with '='
- A formula uses only '+' between terms
- Each term is either an integer literal or a cell name
- Undefined referenced cells should be treated as 0
- If a SET is rejected, the old cell contents and all dependent values must stay unchanged
Examples
Input: [('SET', 'A1', '=A1+1'), ('GET', 'A1')]
Expected Output: [False, 0]
Explanation: Direct self-reference is a cycle and must be rejected.
Input: [('SET', 'A1', '=B1+1'), ('SET', 'B1', '=A1+1'), ('GET', 'A1'), ('GET', 'B1')]
Expected Output: [True, False, 1, 0]
Explanation: The second SET would create a 2-node cycle, so it is rejected.
Input: [('SET', 'B1', 10), ('SET', 'A1', '=B1+5'), ('SET', 'B1', '=A1+1'), ('GET', 'B1'), ('GET', 'A1')]
Expected Output: [True, True, False, 10, 15]
Explanation: Rejected updates must not change existing values.
Input: [('SET', 'A1', '=B1+1'), ('SET', 'B1', '=C1+1'), ('SET', 'C1', '=A1+1'), ('GET', 'C1')]
Expected Output: [True, True, False, 0]
Explanation: This is an indirect 3-cell cycle, so the last SET is rejected.
Input: [('SET', 'A1', '=B1+1'), ('SET', 'A1', 7), ('SET', 'B1', '=A1+1'), ('GET', 'B1')]
Expected Output: [True, True, True, 8]
Explanation: Replacing A1 with an integer removes its dependency, so B1 can later depend on A1 safely.
Hints
- Adding edges from cell X to its referenced cells creates a cycle exactly when one of those referenced cells can already reach X through existing dependency edges.
- Do cycle detection before mutating your dependency graph.