Implement Courier Delivery Cost Tracking
Company: Rippling
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
Quick Answer: This question evaluates skills in time-interval arithmetic, in-memory state management, and billing computation for delivery records, including handling of half-open intervals, duration-to-hour conversions, and prorated payments.
Part 1: Implement Courier Delivery Cost Tracking
Constraints
- 1 <= len(operations) <= 100000
- 0 <= startTime < endTime <= 10^9
- 1 <= hourlyRate <= 10^6
- Each driverId is added at most once
- Every RECORD command refers to a previously added driverId
Examples
Input: ["ADD d1 20", "RECORD d1 0 30", "TOTAL", "RECORD d1 30 90", "TOTAL"]
Expected Output: [10.0, 30.0]
Explanation: The first delivery lasts 30 minutes, so it costs 20 * 30 / 60 = 10. The second lasts 60 minutes and adds 20 more.
Input: ["ADD d1 15", "ADD d2 40", "RECORD d1 0 120", "RECORD d2 10 40", "TOTAL"]
Expected Output: [50.0]
Explanation: d1 costs 15 * 120 / 60 = 30. d2 costs 40 * 30 / 60 = 20. Total = 50.
Input: ["ADD d1 25", "TOTAL"]
Expected Output: [0.0]
Explanation: Edge case: no deliveries have been recorded yet.
Input: ["ADD d1 60", "RECORD d1 5 6", "TOTAL"]
Expected Output: [1.0]
Explanation: A 1-minute delivery at 60 per hour costs exactly 1.
Hints
- Use a hash map from driverId to hourlyRate so each RECORD can find the correct pay rate quickly.
- Because queries only ask for the total cost, you do not need to store every delivery; you can update one running total as deliveries are recorded.
Part 2: Add Payment Support for Accrued and Unpaid Delivery Costs
Constraints
- 1 <= len(operations) <= 5000
- 0 <= startTime < endTime <= 10^9
- 1 <= hourlyRate <= 10^6
- Each driverId is added at most once
- Every RECORD command refers to a previously added driverId
- PAY timestamps may appear in any order, and commands are processed strictly in input order
Examples
Input: ["ADD d1 60", "RECORD d1 0 30", "UNPAID", "PAY 30", "UNPAID"]
Expected Output: [30.0, 0.0]
Explanation: The delivery costs 30 total. After paying up to minute 30, the full delivery has been paid.
Input: ["ADD d1 120", "RECORD d1 0 90", "PAY 60", "UNPAID"]
Expected Output: [60.0]
Explanation: The full delivery costs 180. Paying up to minute 60 covers the first 60 minutes, which costs 120, leaving 60 unpaid.
Input: ["ADD d1 60", "ADD d2 30", "RECORD d1 0 60", "RECORD d2 30 90", "UNPAID", "PAY 45", "UNPAID", "PAY 75", "UNPAID"]
Expected Output: [90.0, 37.5, 7.5]
Explanation: Initially the unpaid total is 60 + 30 = 90. Paying to 45 covers 45 minutes of d1 and 15 minutes of d2. Paying to 75 covers the remaining 15 minutes of d1 and another 30 minutes of d2.
Input: ["ADD d1 60", "RECORD d1 10 20", "PAY 15", "PAY 12", "UNPAID"]
Expected Output: [5.0]
Explanation: Edge case: a later PAY with a smaller timestamp does not undo or double-count anything.
Input: ["ADD d1 60", "PAY 100", "RECORD d1 0 30", "UNPAID"]
Expected Output: [30.0]
Explanation: Edge case for sequential processing: the PAY command happened before the delivery was recorded, so it does not affect that later RECORD.
Hints
- For each delivery, store how far it has already been paid. Initially that boundary is the delivery's start time.
- Keep a running unpaid total. When PAY advances a delivery's paid boundary, subtract only the newly paid slice from that total.