Design a Driver Payroll System
Company: Rippling
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
Quick Answer: This question evaluates a candidate's understanding of data structure design, time-series aggregation, and algorithmic complexity for in-memory accounting of per-driver earnings.
Part 1: Basic Driver Payroll Tracker
Constraints
- 0 <= len(operations) <= 2 * 10^5
- 1 <= hourly_rate <= 10^6
- 0 <= start_ts < end_ts <= 10^9
- Each driver_id is a non-empty string and is registered exactly once
- All query and shift operations refer to already-registered drivers
Examples
Input: [('register_driver', 'd1', 20), ('record_shift', 'd1', 0, 7200), ('get_total_earned', 'd1'), ('register_driver', 'd2', 15), ('record_shift', 'd2', 0, 3600), ('record_shift', 'd1', 7200, 10800), ('get_total_earned', 'd1'), ('get_total_earned', 'd2')]
Expected Output: [40.0, 60.0, 15.0]
Explanation: Driver d1 earns 40 for the first 2-hour shift and 20 for the next 1-hour shift, for a total of 60. Driver d2 earns 15.
Input: [('register_driver', 'solo', 30), ('get_total_earned', 'solo')]
Expected Output: [0.0]
Explanation: Edge case: a registered driver with no recorded shifts has earned 0.
Input: [('register_driver', 'p1', 12), ('record_shift', 'p1', 0, 1800), ('get_total_earned', 'p1')]
Expected Output: [6.0]
Explanation: A 30-minute shift at 12 per hour pays 6.
Input: [('register_driver', 'a', 25), ('record_shift', 'a', 0, 3600), ('get_total_earned', 'a'), ('get_total_earned', 'a')]
Expected Output: [25.0, 25.0]
Explanation: Repeated queries should return the same accumulated total when no new shifts are added.
Hints
- Use one hash map for each driver's hourly rate and another for each driver's running total earned.
- You do not need to store every historical shift in Part 1; when a shift is recorded, its pay can be added immediately.
Part 2: Driver Payroll with Settlement Cutoffs
Constraints
- 0 <= len(operations) <= 2 * 10^5
- 1 <= hourly_rate <= 10^6
- 0 <= start_ts < end_ts <= 10^9
- Each driver_id is a non-empty string and is registered exactly once
- All query and shift operations refer to already-registered drivers
Examples
Input: [('register_driver', 'd1', 20), ('record_shift', 'd1', 0, 3600), ('record_shift', 'd1', 3600, 10800), ('get_unpaid_balance', 'd1'), ('settle_up_to', 4000), ('get_unpaid_balance', 'd1'), ('settle_up_to', 20000), ('get_unpaid_balance', 'd1')]
Expected Output: [60.0, 40.0, 0.0]
Explanation: Before any settlement, both shifts are unpaid for a total of 60. After settling up to 4000, only the first shift is paid. After settling up to 20000, both are paid.
Input: [('register_driver', 'd1', 10), ('register_driver', 'd2', 30), ('record_shift', 'd1', 0, 7200), ('record_shift', 'd2', 0, 3600), ('settle_up_to', 5000), ('get_unpaid_balance', 'd1'), ('get_unpaid_balance', 'd2'), ('record_shift', 'd2', 3600, 7200), ('get_unpaid_balance', 'd2'), ('settle_up_to', 8000), ('get_unpaid_balance', 'd1'), ('get_unpaid_balance', 'd2')]
Expected Output: [20.0, 0.0, 30.0, 0.0, 0.0]
Explanation: The first settlement pays only d2's first shift. Later, after another d2 shift is recorded and settlement reaches 8000, all remaining unpaid shifts are paid.
Input: [('register_driver', 'empty', 25), ('settle_up_to', 100000), ('get_unpaid_balance', 'empty')]
Expected Output: [0.0]
Explanation: Edge case: a driver with no shifts has unpaid balance 0, even after settlement.
Input: [('register_driver', 'x', 10), ('record_shift', 'x', 0, 3600), ('settle_up_to', 5000), ('get_unpaid_balance', 'x'), ('settle_up_to', 1000), ('get_unpaid_balance', 'x')]
Expected Output: [0.0, 0.0]
Explanation: A shift should not be paid twice. A later settlement with a smaller cutoff changes nothing.
Hints
- When a shift is recorded, compute its pay immediately and keep track of it as unpaid for that driver.
- A min-heap ordered by shift end time lets `settle_up_to` remove only the shifts that became payable, instead of scanning every stored shift.