Implement worker time and payroll tracker
Company: Instacart
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: hard
Interview Round: Take-home Project
Quick Answer: This question evaluates skills in designing in-memory data structures and algorithms for temporal event handling, interval arithmetic, stateful toggles, deferred updates (queued promotions), ranking queries, and time-based payroll computation within the Coding & Algorithms domain.
Part 1: Basic Attendance Tracker
Constraints
- 0 <= len(operations) <= 100000
- workerId and title are non-empty strings
- 0 <= salary <= 1000000
- 0 <= timestamp <= 1000000000
- registerTime timestamps are non-decreasing for each worker
- The accumulated total time for any worker fits in a signed 64-bit integer
Examples
Input: ([['addWorker', 'alice', 'Engineer', '50'], ['registerTime', 'alice', '10'], ['registerTime', 'alice', '25'], ['getTotalInOfficeTime', 'alice']],)
Expected Output: [15]
Explanation: Alice has one completed interval [10, 25), so her total is 15.
Input: ([['addWorker', 'bob', 'Analyst', '30'], ['registerTime', 'bob', '0'], ['registerTime', 'bob', '10'], ['registerTime', 'bob', '20'], ['getTotalInOfficeTime', 'bob'], ['registerTime', 'bob', '30'], ['getTotalInOfficeTime', 'bob']],)
Expected Output: [10, 20]
Explanation: After [0, 10), Bob has 10. The open interval starting at 20 is not counted until it closes at 30.
Input: ([['getTotalInOfficeTime', 'ghost'], ['registerTime', 'ghost', '5'], ['addWorker', 'a', 'Dev', '10'], ['addWorker', 'a', 'Lead', '100'], ['registerTime', 'a', '5'], ['getTotalInOfficeTime', 'a']],)
Expected Output: [0, 0]
Explanation: Unknown workers return 0. The duplicate addWorker is ignored. Worker a is currently in office, so the open interval is not counted.
Input: ([['addWorker', 'z', 'Dev', '1'], ['registerTime', 'z', '7'], ['registerTime', 'z', '7'], ['getTotalInOfficeTime', 'z']],)
Expected Output: [0]
Explanation: A zero-length completed interval contributes 0 time.
Input: ([],)
Expected Output: []
Explanation: No operations means no query results.
Hints
- Store each worker's current state: whether they are in office, the current start timestamp, and total completed time.
- Do not add time when an interval starts. Add timestamp - start only when an interval closes.
Part 2: Ranking Workers by Time in Office
Constraints
- 0 <= len(operations) <= 100000
- workerId and title are non-empty strings
- 0 <= salary <= 1000000
- 0 <= timestamp <= 1000000000
- registerTime timestamps are non-decreasing for each worker
- Duplicate addWorker commands are ignored
- registerTime for an unknown worker is ignored
Examples
Input: ([['addWorker', 'a', 'Engineer', '50'], ['registerTime', 'a', '0'], ['registerTime', 'a', '10'], ['addWorker', 'b', 'Engineer', '40'], ['registerTime', 'b', '0'], ['registerTime', 'b', '5'], ['addWorker', 'c', 'Manager', '60'], ['registerTime', 'c', '2'], ['registerTime', 'c', '12'], ['topNByInOfficeTime', '2'], ['topNByInOfficeTimeForTitle', '3', 'Engineer']],)
Expected Output: [['a:10', 'c:10'], ['a:10', 'b:5']]
Explanation: a and c tie at 10, so workerId decides their order. The title-filtered query includes only Engineers.
Input: ([['addWorker', 'b', 'Dev', '1'], ['addWorker', 'a', 'Dev', '1'], ['topNByInOfficeTime', '5']],)
Expected Output: [['a:0', 'b:0']]
Explanation: n is larger than the number of workers. Both have 0 time, so lexicographic workerId order is used.
Input: ([['addWorker', 'x', 'Ops', '10'], ['addWorker', 'y', 'Ops', '10'], ['registerTime', 'x', '10'], ['registerTime', 'y', '1'], ['registerTime', 'y', '4'], ['topNByInOfficeTime', '2']],)
Expected Output: [['y:3', 'x:0']]
Explanation: x is currently in office, but its open interval is not counted.
Input: ([['topNByInOfficeTime', '3'], ['topNByInOfficeTimeForTitle', '2', 'Engineer']],)
Expected Output: [[], []]
Explanation: Ranking with no workers returns an empty list.
Input: ([['addWorker', 'p', 'QA', '10'], ['topNByInOfficeTime', '0'], ['topNByInOfficeTimeForTitle', '-1', 'QA']],)
Expected Output: [[], []]
Explanation: Non-positive n returns an empty ranking.
Hints
- The attendance bookkeeping is the same as Part 1: update total time only when an interval closes.
- For a straightforward solution, build the candidate list at query time and sort by (-totalTime, workerId).
Part 3: Promotions with Deferred Effect
Constraints
- 0 <= len(operations) <= 100000
- workerId and title are non-empty strings
- 0 <= salary <= 1000000
- 0 <= timestamp <= 1000000000
- registerTime timestamps are non-decreasing for each worker
- Duplicate addWorker commands are ignored
- registerTime and promote for unknown workers are ignored
Examples
Input: ([['addWorker', 'a', 'Dev', '50'], ['promote', 'a', 'Senior', '80'], ['getWorkerInfo', 'a'], ['registerTime', 'a', '5'], ['getWorkerInfo', 'a']],)
Expected Output: ['Senior:80:out', 'Senior:80:in']
Explanation: Because a is out of office, the promotion applies immediately.
Input: ([['addWorker', 'a', 'Dev', '50'], ['registerTime', 'a', '0'], ['promote', 'a', 'Lead', '70'], ['getWorkerInfo', 'a'], ['registerTime', 'a', '10'], ['getWorkerInfo', 'a'], ['registerTime', 'a', '20'], ['getWorkerInfo', 'a'], ['getTotalInOfficeTime', 'a']],)
Expected Output: ['Dev:50:in', 'Dev:50:out', 'Lead:70:in', '10']
Explanation: The promotion requested while in office is queued. It is not applied on exit at 10, but it is applied when a enters again at 20.
Input: ([['addWorker', 'a', 'D', '1'], ['registerTime', 'a', '0'], ['promote', 'a', 'M', '2'], ['promote', 'a', 'Dir', '3'], ['registerTime', 'a', '5'], ['registerTime', 'a', '8'], ['getWorkerInfo', 'a']],)
Expected Output: ['Dir:3:in']
Explanation: The second queued promotion overwrites the first queued promotion.
Input: ([['addWorker', 'a', 'Dev', '10'], ['registerTime', 'a', '0'], ['promote', 'a', 'Lead', '20'], ['registerTime', 'a', '5'], ['promote', 'a', 'Principal', '30'], ['getWorkerInfo', 'a'], ['registerTime', 'a', '8'], ['getWorkerInfo', 'a']],)
Expected Output: ['Principal:30:out', 'Principal:30:in']
Explanation: After a exits, a new promotion while out of office applies immediately and clears the older pending promotion.
Input: ([],)
Expected Output: []
Explanation: No operations means no query results.
Hints
- Keep a pending promotion field per worker. While the worker is in office, promote overwrites this field instead of changing current title and salary.
- When registerTime starts a new interval, first apply and clear any pending promotion, then mark the worker as in office.
Part 4: Payroll Query with Effective Salaries
Constraints
- 0 <= len(operations) <= 100000
- workerId and title are non-empty strings
- 0 <= salary <= 1000000
- 0 <= timestamp, startTime, endTime <= 1000000000
- registerTime timestamps are non-decreasing for each worker
- Duplicate addWorker commands are ignored
- registerTime and promote for unknown workers are ignored
- Total pay for any query fits in a signed 64-bit integer
Examples
Input: ([['addWorker', 'a', 'Dev', '10'], ['registerTime', 'a', '0'], ['registerTime', 'a', '10'], ['getPay', 'a', '0', '10'], ['getPay', 'a', '5', '15'], ['getPay', 'a', '10', '20']],)
Expected Output: [100, 50, 0]
Explanation: The completed interval is [0, 10) at rate 10. Only overlaps with the query window are paid.
Input: ([['addWorker', 'a', 'Dev', '10'], ['registerTime', 'a', '0'], ['registerTime', 'a', '5'], ['promote', 'a', 'Senior', '20'], ['registerTime', 'a', '10'], ['registerTime', 'a', '15'], ['getPay', 'a', '0', '20'], ['getPay', 'a', '5', '12']],)
Expected Output: [150, 40]
Explanation: The first interval pays 5*10 = 50. The promotion while out applies immediately, so [10, 15) pays 5*20 = 100.
Input: ([['addWorker', 'a', 'Dev', '10'], ['registerTime', 'a', '0'], ['promote', 'a', 'Senior', '20'], ['registerTime', 'a', '10'], ['getPay', 'a', '0', '20'], ['registerTime', 'a', '20'], ['registerTime', 'a', '25'], ['getPay', 'a', '0', '30'], ['getPay', 'a', '12', '22']],)
Expected Output: [100, 200, 40]
Explanation: The promotion requested during [0, 10) is deferred. It applies when a enters at 20, so [20, 25) uses rate 20.
Input: ([['addWorker', 'x', 'Ops', '5'], ['registerTime', 'x', '10'], ['getPay', 'x', '0', '100'], ['getPay', 'x', '20', '10'], ['getPay', 'ghost', '0', '10']],)
Expected Output: [0, 0, 0]
Explanation: The open interval is incomplete, startTime >= endTime returns 0, and unknown workers return 0.
Input: ([['addWorker', 'a', 'Dev', '10'], ['registerTime', 'a', '0'], ['promote', 'a', 'T2', '20'], ['promote', 'a', 'T3', '30'], ['registerTime', 'a', '10'], ['registerTime', 'a', '15'], ['registerTime', 'a', '16'], ['getPay', 'a', '0', '20']],)
Expected Output: [130]
Explanation: Only the latest queued promotion matters. [0, 10) pays 100 and [15, 16) pays 30.
Hints
- Store each completed interval with its start, end, and effective salary rate.
- For faster getPay, maintain sorted interval end times and prefix sums of full interval pay, then compute pay before T with binary search.