Design a Stateful Working Hours Register
AirbnbSoftware Engineer · Online AssessmentLast updated
Design an in-memory working hours and compensation tracking register that supports badge events, top-k leaderboards, scheduled rate changes, and interval-based historical earnings calculations.
Problem Statement
Design an in-memory system, WorkingHoursRegister, that tracks employee badge events, calculates completed working time, ranks employees by productivity, and deterministically computes historical compensation across delayed pay-rate updates.
Implement the following methods:
-
register_employee(emp_id: str, initial_hourly_rate: int) -> boolRegisters a new employee with their starting pay rate (in USD per hour). ReturnsFalseifemp_idis already registered; otherwise registers the employee and returnsTrue. -
badge_in(emp_id: str, timestamp: int) -> boolBadges an employee into work. ReturnsFalseif the employee does not exist, is currently badged in, ortimestampis less than or equal to the employee's last recorded badge event timestamp. Otherwise, records the event and returnsTrue. -
badge_out(emp_id: str, timestamp: int) -> boolBadges an employee out. ReturnsFalseif the employee does not exist, is not currently badged in, ortimestampis less than or equal to the correspondingbadge_intimestamp. Otherwise, completes the shift[badge_in_time, timestamp]and returnsTrue. -
get_top_k_employees(k: int) -> list[str]Returns the IDs of the topkemployees who have accumulated the highest total completed working seconds across all completed shifts. Active (open) badge-in sessions do not count. If two employees have the exact same total completed time, tie-break lexicographically ascending byemp_id. If fewer thankemployees exist, return all registered employees sorted accordingly. -
schedule_promotion(emp_id: str, effective_time: int, new_hourly_rate: int) -> boolSchedules a pay-rate change foremp_idtaking effect attimestamp >= effective_time. Multiple promotions can be scheduled in any order, but no two promotions for the same employee may have the sameeffective_time. ReturnsFalseif the employee is not registered,new_hourly_rate floatComputes total earnings foremp_idstrictly within the query window[start_time, end_time). Only completed shifts overlapping with[start_time, end_time)are considered. If a shift crosses promotion boundaries or window boundaries, split the shift and apply the respective hourly rate for each sub-interval. Earnings for a sub-interval ofduration_secondsatratedollars/hour equal(duration_seconds / 3600.0) * rate. Return the total earnings rounded to 2 decimal places. Ifemp_iddoes not exist orstart_time >= end_time, return0.0.
Example
register = WorkingHoursRegister()
register.register_employee("alice", 36) # $36/hr = $0.01/sec
register.register_employee("bob", 72) # $72/hr = $0.02/sec
register.badge_in("alice", 0)
register.badge_out("alice", 3600) # alice works 3600 seconds
register.badge_in("bob", 1000)
register.badge_out("bob", 2800) # bob works 1800 seconds
# Top 1 employee
register.get_top_k_employees(1) # ["alice"]
# Schedule a promotion for Alice at t = 5000 to $72/hr
register.schedule_promotion("alice", 5000, 72)
# Alice works across the promotion boundary
register.badge_in("alice", 4000)
register.badge_out("alice", 6000)
# Worked 4000..5000 (1000s at $36/hr = $10.00) + 5000..6000 (1000s at $72/hr = $20.00) = $30.00
# Query Alice earnings for 0..7000:
# Shift 1 (0..3600 at $36/hr): $36.00
# Shift 2 (4000..6000): $30.00
# Total: 66.00
register.calculate_earnings("alice", 0, 7000) # 66.0
# Query Alice earnings within a restricted window [3000, 5500):
# Shift 1 overlap: [3000, 3600) = 600s @ $36/hr = $6.00
# Shift 2 overlap: [4000, 5000) = 1000s @ $36/hr = $10.00
# [5000, 5500) = 500s @ $72/hr = $10.00
# Total: 6.00 + 10.00 + 10.00 = 26.00
register.calculate_earnings("alice", 3000, 5500) # 26.0
Constraints
0 <= timestamp, start_time, end_time, effective_time <= 10^9- Total operations across all methods
<= 50,000 - Maximum number of registered employees
<= 1,000 1 <= k <= 1,0001 <= initial_hourly_rate, new_hourly_rate <= 1,000- Badge timestamps for any single employee are strictly increasing across events.
Hints
Hint 1
Maintain each employee's completed work intervals and rate-change timelines in sorted order to allow binary search during earnings calculation.
Hint 2
For top-k queries, maintain total completed hours per employee and update upon each valid badge_out, or sort dynamically if top-k calls are infrequent relative to badge events.
Hint 3
When computing earnings for an interval, intersect the query interval with each completed shift, then slice each resulting interval by any promotion timestamps falling within it.
Follow-up questions
- How would you optimize `calculate_earnings` if an employee had tens of thousands of short completed shifts?
- How would you handle out-of-order or backfilled badge corrections (e.g., retroactively fixing a missed badge_out)?
- How would the system scale if badge events were ingested concurrently from thousands of physical office card readers?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.