Compute party hours per town and city gaps
Company: Scale AI
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
Quick Answer: This question evaluates the ability to manipulate time-interval data, compute interval unions and gaps, and perform hierarchical aggregation across towns and cities, testing skills in interval arithmetic, data modeling, and efficient algorithmic implementation.
Part 1: Unioned Party Hours Per Town
Constraints
- 0 <= len(region_data) <= 10^5
- 0 <= len(party_intervals) <= 2 * 10^5
- Times are integer minutes.
- Intervals may be unsorted, overlapping, touching, or partially outside the day window.
- Treat touching intervals like [10, 20] and [20, 30] as one continuous covered span.
- Assume each town_id in region_data is unique.
Examples
Input: ([('t1', 'Oak', 'c1', 'Metro', 's1', 'North'), ('t2', 'Pine', 'c1', 'Metro', 's1', 'North')], [('t1', 60, 120), ('t1', 90, 180), ('t1', 180, 210), ('t2', 300, 330)], 0, 1440)
Expected Output: [('t1', 2.5), ('t2', 0.5)]
Explanation: Town t1 has merged coverage [60, 210] = 150 minutes = 2.5 hours. Town t2 has 30 minutes = 0.5 hours.
Input: ([('t1', 'Oak', 'c1', 'Metro', 's1', 'North'), ('t2', 'Pine', 'c1', 'Metro', 's1', 'North'), ('t3', 'Elm', 'c2', 'Lake', 's1', 'North')], [('t1', -30, 30), ('t1', 1400, 1500), ('t3', 100, 160), ('t3', 120, 180)], 0, 1440)
Expected Output: [('t1', 1.17), ('t2', 0.0), ('t3', 1.33)]
Explanation: t1 is clamped to [0,30] and [1400,1440] for 70 minutes total. t2 has no intervals. t3 merges to [100,180] for 80 minutes.
Input: ([('t1', 'Solo', 'c1', 'Metro', 's1', 'North')], [('t1', -50, 100), ('t1', 100, 400), ('t1', 300, 1440), ('t1', 1440, 1500)], 0, 1440)
Expected Output: [('t1', 24.0)]
Explanation: After clamping and merging, the town is covered for the full day [0,1440).
Input: ([('t1', 'Oak', 'c1', 'Metro', 's1', 'North'), ('t2', 'Pine', 'c2', 'Lake', 's1', 'North')], [('t1', 450, 500), ('t1', 590, 650), ('t2', 480, 600)], 480, 600)
Expected Output: [('t1', 0.5), ('t2', 2.0)]
Explanation: The day window is [480,600). t1 contributes [480,500] and [590,600] = 30 minutes. t2 covers the whole window.
Hints
- Group intervals by town_id, then sort each town's intervals by start time before merging.
- Clamp each interval to the day window first; after clamping, ignore intervals where start >= end.
Part 2: City-Grouped Idle Segments Per Town
Constraints
- 0 <= len(region_data) <= 10^5
- 0 <= len(party_intervals) <= 2 * 10^5
- Times are integer minutes.
- Intervals may be unsorted, overlapping, touching, or partially outside the day window.
- Touching party intervals must be merged before computing gaps.
- Assume each town_id in region_data belongs to exactly one city_id.
Examples
Input: ([('t1', 'Oak', 'c1', 'Metro', 's1', 'North'), ('t2', 'Pine', 'c1', 'Metro', 's1', 'North'), ('t3', 'Elm', 'c2', 'Lake', 's1', 'North')], [('t1', 60, 120), ('t1', 90, 180), ('t2', 300, 360), ('t3', 0, 600)], 0, 600)
Expected Output: {'c1': [('t1', 0, 60), ('t1', 180, 600), ('t2', 0, 300), ('t2', 360, 600)], 'c2': []}
Explanation: t1 has idle [0,60] and [180,600]. t2 has idle [0,300] and [360,600]. t3 is busy for the full day window, so c2 gets an empty list.
Input: ([('t1', 'Oak', 'c1', 'Metro', 's1', 'North'), ('t2', 'Pine', 'c1', 'Metro', 's1', 'North'), ('t3', 'Elm', 'c2', 'Lake', 's1', 'North')], [('t1', -30, 30), ('t1', 90, 150), ('t3', 50, 70), ('t3', 70, 80)], 0, 120)
Expected Output: {'c1': [('t1', 30, 90), ('t2', 0, 120)], 'c2': [('t3', 0, 50), ('t3', 80, 120)]}
Explanation: t1 becomes busy on [0,30] and [90,120], leaving [30,90]. t2 has no intervals, so it is idle the whole day. t3's touching intervals merge to [50,80].
Input: ([('t1', 'Solo', 'c1', 'Metro', 's1', 'North')], [], 10, 20)
Expected Output: {'c1': [('t1', 10, 20)]}
Explanation: With no party intervals, the town is idle for the entire day window.
Input: ([('t1', 'Oak', 'c1', 'Metro', 's1', 'North'), ('t2', 'Pine', 'c1', 'Metro', 's1', 'North')], [('t1', 10, 20), ('t1', 20, 30), ('t1', 25, 40), ('t2', 0, 100)], 0, 100)
Expected Output: {'c1': [('t1', 0, 10), ('t1', 40, 100)]}
Explanation: t1's touching and overlapping intervals merge to [10,40], so there is no gap at 20. t2 covers the whole day and contributes no idle segment.
Hints
- First solve the per-town merge problem: city grouping is easier once each town has a clean merged interval list.
- Idle segments are just the complement of merged party coverage inside [day_start, day_end).