Evaluate Symbol Expressions
Company: Harvey
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Onsite
Quick Answer: This question evaluates the ability to parse and evaluate arithmetic expressions with symbol references, perform recursive dependency resolution, and detect invalid references and circular dependencies.
Part 1: Evaluate All Symbol Expressions
Constraints
- 0 <= number of symbols <= 1000
- Total length of all expression strings <= 100000
- Expressions contain only integer constants, valid symbol names, parentheses, spaces, and operators +, -, *, /
- Expressions are syntactically valid
- Test cases do not include division by zero
Examples
Input: {'a': '3', 'b': 'a + 2', 'c': 'b * 4', 'd': 'c - a'}
Expected Output: {'a': 3, 'b': 5, 'c': 20, 'd': 17}
Explanation: A simple dependency chain. Each symbol depends on previously computed symbols.
Input: {'x': '8', 'y': '(x + 4) / 3', 'z': 'y * 2'}
Expected Output: {'x': 8, 'y': 4.0, 'z': 8.0}
Explanation: Shows parentheses and division. The / operator uses floating-point division.
Input: {'only': '42'}
Expected Output: {'only': 42}
Explanation: Edge case: a single symbol with no dependencies.
Input: {'a': 'b + 1'}
Expected Output: 'ValueError: Unknown symbol: b'
Explanation: This case should raise ValueError because symbol b does not exist.
Input: {'a': 'b + 1', 'b': 'c + 1', 'c': 'a + 1'}
Expected Output: 'ValueError: Circular dependency detected at: a'
Explanation: This case should raise ValueError because a -> b -> c -> a forms a cycle.
Hints
- Use DFS with memoization so each symbol is evaluated only once.
- Keep a recursion-stack set (or color map) to detect when evaluation re-enters a symbol that is already being processed.
Part 2: Detect Circular Symbol Dependencies
Constraints
- 0 <= number of symbols <= 1000
- Total length of all expression strings <= 100000
- Expressions are syntactically valid
- Only references to symbols present in the dictionary create graph edges
Examples
Input: {'a': 'b + 1', 'b': 'c + 1', 'c': 'a + 1'}
Expected Output: True
Explanation: a depends on b, b depends on c, and c depends back on a.
Input: {'a': '3', 'b': 'a + 2', 'c': 'b * 4', 'd': 'c - a'}
Expected Output: False
Explanation: This dependency chain is acyclic.
Input: {'a': 'a + 1'}
Expected Output: True
Explanation: Edge case: a direct self-reference is a cycle.
Input: {}
Expected Output: False
Explanation: Edge case: an empty map has no cycle.
Input: {'a': 'missing + 1', 'b': 'a + 2'}
Expected Output: False
Explanation: The name 'missing' is ignored for this problem because it is not a defined symbol, so the remaining graph is acyclic.
Hints
- First extract which symbol names each expression references, then build a graph.
- Use DFS with three states: unvisited, visiting, and visited. Reaching a 'visiting' node means a cycle exists.