Skip to main content
Back to problems
#3635
Medium Algorithms

Earliest finish time for land and water rides ii

Array Two Pointers Binary Search Greedy Sorting
35.0% acceptance
Feb 25, 2026
72
2
You are given two categories of theme park attractions: land rides and water rides. Land rides landStartTime[i] – the earliest time the ith land ride can be boarded. landDuration[i] – how long the ith land ride lasts. Water rides waterStartTime[j] – the earliest time the jth water ride can be boarded. waterDuration[j] – how long the jth water ride lasts. A tourist must experience exactly one ride from each category, in either order. A ride may be started at its opening time or any later moment. If a ride is started at time t, it finishes at time t + duration. Immediately after finishing one ride the tourist may board the other (if it is already open) or wait until it opens. Return the earliest possible time at which the tourist can finish both rides.

Solution

Rust
Time O(n log n)
Space O(n)
LeetCode
solution.rs
impl Solution {
  pub fn earliest_finish_time(
    land_start_time: Vec<i32>,
    land_duration: Vec<i32>,
    water_start_time: Vec<i32>,
    water_duration: Vec<i32>,
  ) -> i32 {
    let n = land_start_time.len();
    let m = water_start_time.len();
    // land_end[i] = land_start_time[i] + land_duration[i]
    // water_end[j] = water_start_time[j] + water_duration[j]
    // For land->water: finish = max(land_end[i], water_start_time[j]) + water_duration[j]
    //   = land_end[i] + water_duration[j]  if land_end[i] >= water_start_time[j]
    //   = water_end[j]                       if land_end[i] < water_start_time[j]
    // For water->land: finish = max(water_end[j], land_start_time[i]) + land_duration[i]
    //   = water_end[j] + land_duration[i]   if water_end[j] >= land_start_time[i]
    //   = land_end[i]                        if water_end[j] < land_start_time[i]
    //
    // Sort by land_end and water_start_time for efficient computation.

    // Compute land finishes
    let land_end: Vec<i32> = (0..n).map(|i| land_start_time[i] + land_duration[i]).collect();
    let water_end: Vec<i32> = (0..m).map(|j| water_start_time[j] + water_duration[j]).collect();

    // Sort water by start time for binary search
    let mut water_sorted_by_start: Vec<(i32, i32)> = (0..m).map(|j| (water_start_time[j], water_end[j])).collect();
    water_sorted_by_start.sort_unstable();
    // Prefix max of water_end sorted by start time
    // For land->water: if land_end[i] < water_start[j], finish = water_end[j]
    //   We want to minimize: for all j with water_start[j] > land_end[i], min(water_end[j])
    // For land->water if land_end[i] >= water_start[j], finish = land_end[i] + water_duration[j]
    //   = land_end[i] + (water_end[j] - water_start[j])
    //   We want to minimize water_duration[j] for j with water_start[j] <= land_end[i]
    //   i.e. min(water_end[j] - water_start[j]) for j with water_start[j] <= land_end[i]
    
    let ws_starts: Vec<i32> = water_sorted_by_start.iter().map(|&(s,_)| s).collect();
    let ws_ends: Vec<i32> = water_sorted_by_start.iter().map(|&(_,e)| e).collect();
    let ws_durs: Vec<i32> = water_sorted_by_start.iter().zip(water_end.iter()).map(|(&(s,e), _)| e - s).collect();

    // prefix min duration (water_end - water_start) for water rides with start <= threshold
    let mut prefix_min_dur = vec![i32::MAX; m + 1];
    for j in 0..m {
      prefix_min_dur[j + 1] = prefix_min_dur[j].min(ws_durs[j]);
    }
    // suffix min of water_end for water rides sorted by start
    let mut suffix_min_end = vec![i32::MAX; m + 1];
    for j in (0..m).rev() {
      suffix_min_end[j] = suffix_min_end[j + 1].min(ws_ends[j]);
    }

    // Similarly for water->land
    let mut land_sorted_by_start: Vec<(i32, i32)> = (0..n).map(|i| (land_start_time[i], land_end[i])).collect();
    land_sorted_by_start.sort_unstable();
    let ls_starts: Vec<i32> = land_sorted_by_start.iter().map(|&(s,_)| s).collect();
    let ls_ends: Vec<i32> = land_sorted_by_start.iter().map(|&(_,e)| e).collect();
    let ls_durs: Vec<i32> = land_sorted_by_start.iter().map(|&(s,e)| e - s).collect();

    let mut prefix_min_dur_l = vec![i32::MAX; n + 1];
    for i in 0..n {
      prefix_min_dur_l[i + 1] = prefix_min_dur_l[i].min(ls_durs[i]);
    }
    let mut suffix_min_end_l = vec![i32::MAX; n + 1];
    for i in (0..n).rev() {
      suffix_min_end_l[i] = suffix_min_end_l[i + 1].min(ls_ends[i]);
    }

    let mut best = i32::MAX;

    // For each land ride, find best water pairing
    for i in 0..n {
      let le = land_end[i];
      // Binary search: last water with start <= le
      let pos = ws_starts.partition_point(|&s| s <= le);
      // Case 1: water_start <= land_end => finish = land_end + min_water_dur
      if pos > 0 {
        let min_dur = prefix_min_dur[pos];
        if min_dur < i32::MAX {
          best = best.min(le + min_dur);
        }
      }
      // Case 2: water_start > land_end => finish = min(water_end for those j)
      if pos < m {
        let min_end = suffix_min_end[pos];
        if min_end < i32::MAX {
          best = best.min(min_end);
        }
      }
    }

    // For each water ride, find best land pairing
    for j in 0..m {
      let we = water_end[j];
      let pos = ls_starts.partition_point(|&s| s <= we);
      // Case 1: land_start <= water_end => finish = water_end + min_land_dur
      if pos > 0 {
        let min_dur = prefix_min_dur_l[pos];
        if min_dur < i32::MAX {
          best = best.min(we + min_dur);
        }
      }
      // Case 2: land_start > water_end => finish = min(land_end for those i)
      if pos < n {
        let min_end = suffix_min_end_l[pos];
        if min_end < i32::MAX {
          best = best.min(min_end);
        }
      }
    }

    best
  }
}