PracHub
QuestionsCoachesLearningGuidesInterview Prep

Quick Overview

This question evaluates a candidate's ability to design a stateful, multi-part class that combines interval tracking with incremental balance accounting, a common pattern in coding interviews for backend systems roles. It tests practical application skills across data structure design, running-total bookkeeping, and computing maximum overlap within a time window, moving from straightforward bookkeeping to a harder interval-scheduling extension.

  • medium
  • Rippling
  • Coding & Algorithms
  • Software Engineer

Courier Delivery Cost Tracker

Company: Rippling

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Technical Screen

# Courier Delivery Cost Tracker You are building the back end for a courier company's dispatch and payouts system. It tracks the deliveries that drivers perform, how much each driver is owed, how much has already been paid out, and how busy the fleet is over time. Implement a class `DeliverySystem` supporting the operations below. The three parts build on the same internal state, so implement them in order. Time is represented as integer timestamps (think "seconds since an epoch"). Every delivery occupies the **half-open** interval `[startTime, endTime)` — the delivery is active at `startTime` but no longer active at `endTime`. Assume a single driver never has two overlapping deliveries (a driver does one delivery at a time). ## Part 1 — Recording deliveries and total cost - `addDriver(driverId)` — Register a new driver. You may assume each `driverId` is added exactly once, before any delivery is recorded for it. - `recordDelivery(driverId, startTime, endTime, cost)` — Record a completed delivery by `driverId` that was active during `[startTime, endTime)` and costs `cost` dollars (money the company owes the driver). Guaranteed `startTime < endTime` and `cost >= 0`. - `getTotalCost(driverId)` — Return the sum of `cost` across every delivery recorded for `driverId`. Return `0` if the driver has no deliveries. Note: you must persist each delivery's `startTime` and `endTime`. They are not needed by `getTotalCost`, but Part 3 depends on them. ## Part 2 — Payouts and unpaid balance The company pays drivers incrementally. - `payUpTo(driverId, amount)` — Pay the driver up to `amount` dollars toward their outstanding (unpaid) balance. The amount actually paid is `min(amount, currentUnpaidBalance)` — you can never pay out more than what is currently owed. Decrease the driver's unpaid balance by the amount paid and **return the amount actually paid**. Guaranteed `amount >= 0`. - `getTotalCostUnpaid(driverId)` — Return the driver's current unpaid balance: the total cost of all of their deliveries minus everything paid to them so far via `payUpTo`. Return `0` if nothing is owed. ## Part 3 — Peak concurrency in a window Operations teams want to know the busiest moment of the day. - `maxConcurrentDrivers(windowStart, windowEnd)` — Consider every delivery (across all drivers) that overlaps the half-open query window `[windowStart, windowEnd)`, clipping each overlapping delivery to the window. Find the maximum number of deliveries that were simultaneously active at any instant inside the window. Because a driver has at most one active delivery at a time, this count equals the maximum number of **distinct drivers** delivering at the same time. Return a triple `[maxCount, peakStart, peakEnd]`, where `[peakStart, peakEnd)` is the time sub-interval inside the window during which exactly `maxCount` deliveries were continuously active. If several disjoint sub-intervals reach the maximum, return the one with the smallest `peakStart`. If no delivery overlaps the window, return `[0, -1, -1]`. The query window is typically 24 hours wide, but your solution should work for any `windowStart < windowEnd`. ## Example ```text ds = DeliverySystem() ds.addDriver(1) ds.addDriver(2) ds.recordDelivery(1, 0, 100, 50) # driver 1 active on [0, 100), cost 50 ds.recordDelivery(1, 100, 200, 30) # driver 1 active on [100, 200), cost 30 ds.recordDelivery(2, 50, 150, 40) # driver 2 active on [50, 150), cost 40 ds.getTotalCost(1) # -> 80 (50 + 30) ds.getTotalCost(2) # -> 40 ds.payUpTo(1, 60) # pays min(60, 80) = 60 -> returns 60 ds.getTotalCostUnpaid(1) # -> 20 (80 - 60) ds.payUpTo(1, 100) # pays min(100, 20) = 20 -> returns 20 ds.getTotalCostUnpaid(1) # -> 0 ds.maxConcurrentDrivers(0, 300) # -> [2, 50, 150] # Active deliveries over time: # [0, 50): driver 1 -> 1 active # [50, 100): driver 1, driver 2 -> 2 active # [100, 150): driver 1, driver 2 -> 2 active (driver 1 swaps to its next delivery at t=100, but the # count of distinct drivers delivering does not drop) # [150, 200): driver 1 -> 1 active # Maximum concurrency = 2, continuously sustained across [50, 150) (the count never drops below 2 # anywhere in that span, even though the underlying delivery instance for driver 1 changes at t=100). ``` ## Constraints - At most $10^5$ total operations. - $1 \le \texttt{driverId} \le 10^9$. - $0 \le \texttt{startTime} < \texttt{endTime} \le 10^9$ and $0 \le \texttt{windowStart} < \texttt{windowEnd} \le 10^9$. - $0 \le \texttt{cost}, \texttt{amount} \le 10^6$. - Every `driverId` passed to `recordDelivery`, `getTotalCost`, `payUpTo`, and `getTotalCostUnpaid` has been registered via `addDriver` first.

Quick Answer: This question evaluates a candidate's ability to design a stateful, multi-part class that combines interval tracking with incremental balance accounting, a common pattern in coding interviews for backend systems roles. It tests practical application skills across data structure design, running-total bookkeeping, and computing maximum overlap within a time window, moving from straightforward bookkeeping to a harder interval-scheduling extension.

Implement a class `DeliverySystem` for a courier company's dispatch and payouts back end. It tracks each driver's deliveries, how much each driver is owed, how much has been paid, and how busy the fleet is over time. The three parts share the same internal state, so build them in order. Time is integer timestamps. Every delivery occupies the **half-open** interval `[startTime, endTime)` — active at `startTime`, not at `endTime`. A single driver never has two overlapping deliveries (one delivery at a time). **Part 1 — recording deliveries and total cost** - `addDriver(driverId)` — register a new driver (added exactly once, before any of its deliveries). - `recordDelivery(driverId, startTime, endTime, cost)` — record a delivery active on `[startTime, endTime)` costing `cost` dollars owed to the driver (`startTime < endTime`, `cost >= 0`). You must persist `startTime`/`endTime` — Part 3 needs them. - `getTotalCost(driverId)` — sum of `cost` over all of that driver's deliveries (`0` if none). **Part 2 — payouts and unpaid balance** - `payUpTo(driverId, amount)` — pay `min(amount, currentUnpaidBalance)` toward the outstanding balance, decrease the balance by that, and **return the amount actually paid** (`amount >= 0`). - `getTotalCostUnpaid(driverId)` — total cost of the driver's deliveries minus everything paid so far (`0` if nothing is owed). **Part 3 — peak concurrency in a window** - `maxConcurrentDrivers(windowStart, windowEnd)` — over every delivery (all drivers) that overlaps the half-open window `[windowStart, windowEnd)`, clipped to the window, find the maximum number simultaneously active at any instant (equivalently, the max number of distinct drivers delivering at once). Return `[maxCount, peakStart, peakEnd]` where `[peakStart, peakEnd)` is the sub-interval during which exactly `maxCount` deliveries stay continuously active; if several disjoint sub-intervals tie, return the one with the smallest `peakStart`. If nothing overlaps, return `[0, -1, -1]`. Because deliveries are half-open, a driver swapping from one delivery to the next at the same instant keeps the count from dropping. --- **Console I/O contract.** Because this is a multi-method class, the judge drives it through a single entry point `solution(operations, args)`: - `operations` is a list of method-name strings, e.g. `"recordDelivery"`. - `args` is a parallel list; `args[i]` holds the arguments for `operations[i]`. - Return a list of per-operation results: the returned value for query methods, and `null`/`None` for the void methods `addDriver` and `recordDelivery`. Example: `operations = ["addDriver","recordDelivery","getTotalCost"]`, `args = [[1],[1,0,100,50],[1]]` → `[null, null, 50]`.

Constraints

  • At most 10^5 total operations.
  • 1 <= driverId <= 10^9.
  • 0 <= startTime < endTime <= 10^9 and 0 <= windowStart < windowEnd <= 10^9.
  • 0 <= cost, amount <= 10^6.
  • Every driverId passed to recordDelivery/getTotalCost/payUpTo/getTotalCostUnpaid was registered via addDriver first.
  • A single driver never has two overlapping deliveries.

Examples

Input: (['addDriver', 'addDriver', 'recordDelivery', 'recordDelivery', 'recordDelivery', 'getTotalCost', 'getTotalCost', 'payUpTo', 'getTotalCostUnpaid', 'payUpTo', 'getTotalCostUnpaid', 'maxConcurrentDrivers'], [[1], [2], [1, 0, 100, 50], [1, 100, 200, 30], [2, 50, 150, 40], [1], [2], [1, 60], [1], [1, 100], [1], [0, 300]])

Expected Output: [None, None, None, None, None, 80, 40, 60, 20, 20, 0, [2, 50, 150]]

Explanation: Mirrors the prompt example: costs 80/40, incremental payouts of 60 then 20 drive the unpaid balance to 0, and peak concurrency of 2 is sustained continuously across [50,150) even though driver 1 swaps deliveries at t=100.

Input: (['addDriver', 'getTotalCost', 'getTotalCostUnpaid', 'payUpTo', 'getTotalCostUnpaid', 'maxConcurrentDrivers'], [[5], [5], [5], [5, 100], [5], [0, 100]])

Expected Output: [None, 0, 0, 0, 0, [0, -1, -1]]

Explanation: A registered driver with zero deliveries owes nothing, so getTotalCost/Unpaid are 0 and payUpTo pays 0. With no deliveries recorded, the window has no coverage: [0,-1,-1].

Input: (['addDriver', 'recordDelivery', 'maxConcurrentDrivers', 'maxConcurrentDrivers', 'maxConcurrentDrivers'], [[1], [1, 10, 50, 7], [30, 40], [60, 70], [0, 20]])

Expected Output: [None, None, [1, 30, 40], [0, -1, -1], [1, 10, 20]]

Explanation: One delivery on [10,50). Window [30,40) clips it to [30,40); window [60,70) does not overlap so returns [0,-1,-1]; window [0,20) clips to [10,20).

Input: (['addDriver', 'addDriver', 'addDriver', 'recordDelivery', 'recordDelivery', 'recordDelivery', 'recordDelivery', 'maxConcurrentDrivers', 'getTotalCost'], [[1], [2], [3], [1, 0, 10, 5], [2, 0, 10, 5], [1, 20, 30, 5], [3, 20, 30, 5], [0, 30], [1]])

Expected Output: [None, None, None, None, None, None, None, [2, 0, 10], 10]

Explanation: Concurrency reaches 2 on both [0,10) and [20,30) with a lull between; the tie is broken by the smallest peakStart, so [2,0,10]. Driver 1's two deliveries total 10.

Input: (['addDriver', 'addDriver', 'addDriver', 'recordDelivery', 'recordDelivery', 'recordDelivery', 'recordDelivery', 'maxConcurrentDrivers', 'getTotalCost', 'getTotalCostUnpaid', 'payUpTo', 'getTotalCostUnpaid'], [[1], [2], [3], [1, 0, 100, 50], [1, 100, 200, 30], [2, 50, 150, 40], [3, 60, 140, 20], [0, 300], [1], [1], [1, 1000], [1]])

Expected Output: [None, None, None, None, None, None, None, [3, 60, 140], 80, 80, 80, 0]

Explanation: Three drivers overlap on [60,140) for a peak of 3, held continuously through driver 1's delivery swap at t=100. payUpTo(1,1000) is capped at the 80 owed and returns 80, zeroing the unpaid balance.

Hints

  1. Parts 1 and 2 are pure bookkeeping: keep a per-driver running total and a per-driver paid amount. Unpaid balance is just total - paid, and payUpTo pays min(amount, unpaid).
  2. Persist every delivery's (startTime, endTime) even though getTotalCost ignores them — Part 3 needs the full set across all drivers.
  3. For Part 3, use a sweep line: clip each delivery to the window, add +1 at the clipped start and -1 at the clipped end of a boundary map, then sweep the sorted coordinates accumulating the running count.
  4. Because the intervals are half-open, +1 at a start and -1 at an end that fall on the same coordinate cancel, so a driver swapping deliveries at the same instant does not drop the count.
  5. Find the max running count, then return the FIRST maximal run of contiguous coordinate-intervals whose count equals the max — that gives the smallest peakStart and the continuous [peakStart, peakEnd). Return [0, -1, -1] when no delivery overlaps the window.
Last updated: Jul 1, 2026

Loading coding console...

PracHub

Master your tech interviews with 8,000+ real questions from top companies.

Product

  • Questions
  • Learning Tracks
  • Interview Guides
  • Resources
  • Premium
  • For Universities
  • Student Access

Browse

  • By Company
  • By Role
  • By Category
  • Topic Hubs
  • SQL Questions
  • AI Coding Questions
  • Compare Platforms
  • Discord Community

Support

  • support@prachub.com
  • (916) 541-4762

Legal

  • Privacy Policy
  • Terms of Service
  • About Us

© 2026 PracHub. All rights reserved.

Related Coding Questions

  • Implement a Searchable Logger Pipeline - Rippling (hard)
  • Implement an In-Memory File System - Rippling
  • Compare Complete and Partial Poker Hands - Rippling (medium)
  • Implement Courier Delivery Cost Tracking - Rippling (medium)
  • Implement Article Vote Tracking - Rippling (medium)