Quick Overview

Merge straight lane segments, each given as a list of collinear integer points, so that segments lying on the same line whose extents overlap or touch become one segment containing all of their points. It tests exact collinearity checks, handling of horizontal and vertical segments, and interval merging along each line.

Merge Overlapping Collinear Lane Segments Given as Integer Point Lists

Company: Applied Intuition

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: hard

Interview Round: Technical Screen

Lane geometry is given as a list of straight lane segments. Each segment is a list of integer points `[x, y]` that all lie on one straight line, listed in order along it. Several segments may lie on the same line and overlap. Merge every group of overlapping segments that lie on the same line into one segment containing all of their points, and return the resulting segments. The interviewer specifically asked how the approach handles a segment whose slope is 0. Your function must handle every orientation, including horizontal and vertical segments. ### Function Signature ```python def merge_lane_segments(segments: list[list[list[int]]]) -> list[list[list[int]]]: ``` `segments[i]` is the list of points of segment `i`, and each point is `[x, y]`. ### Rules - Two segments lie on the same line when all of their points are collinear. Segments on different lines are never merged, even if they cross, touch or are parallel. - The extent of a segment is the closed stretch of its line from its first point to its last point. Two segments on the same line overlap when their extents share at least one point; touching at a single endpoint counts. - Merging is transitive: if A overlaps B and B overlaps C, all three become one segment, even when A and C do not overlap. - Each output segment, merged or not, is the list of the distinct points of its member segments, sorted by `x` ascending and then by `y` ascending. - Output order: group the output segments by line, and order the lines by the index of the first input segment that lies on each line. Within one line, order the output segments by their first point, by `x` and then by `y`. ### Constraints - `1 <= len(segments) <= 10^4` - `2 <= len(segments[i]) <= 100`, and the total number of points over all segments is at most `10^5`. - Every coordinate is an integer in `[-10^4, 10^4]`. - Within a segment, the points are distinct, collinear, and sorted by `x` ascending and then by `y` ascending. - Different segments may share points or be identical. ### Examples **Example 1** ```text Input: segments = [[[1, 1], [2, 2], [4, 4]], [[2, 1], [4, 2]], [[3, 3], [6, 6]], [[7, 7], [8, 8]]] Output: [[[1, 1], [2, 2], [3, 3], [4, 4], [6, 6]], [[7, 7], [8, 8]], [[2, 1], [4, 2]]] ``` Segments 0, 2 and 3 lie on the line `y = x`, and segment 1 lies on `y = x / 2`. On `y = x`, segment 0 spans `x` from 1 to 4 and segment 2 spans 3 to 6, so they merge into one segment holding the points of both; segment 3 spans 7 to 8 and stays separate. The line `y = x` holds input segment 0, so its output segments come before the one on `y = x / 2`. **Example 2** ```text Input: segments = [[[0, 0], [2, 0]], [[1, -2], [1, 0]], [[2, 0], [5, 0]], [[1, 3], [1, 5]], [[6, 0], [7, 0]]] Output: [[[0, 0], [2, 0], [5, 0]], [[6, 0], [7, 0]], [[1, -2], [1, 0]], [[1, 3], [1, 5]]] ``` Segments 0 and 2 lie on the horizontal line `y = 0` and touch at `(2, 0)`, so they merge; segment 4 starts at `x = 6`, after the merged extent ends at `x = 5`, so it stays separate. Segments 1 and 3 lie on the vertical line `x = 1` and do not overlap. Segment 1 ends at `(1, 0)`, a point inside segment 0, but it lies on a different line, so the two are not merged. **Example 3** ```text Input: segments = [[[0, 0], [2, 1], [6, 3]], [[0, 1], [2, 2]], [[2, 1], [4, 2]]] Output: [[[0, 0], [2, 1], [4, 2], [6, 3]], [[0, 1], [2, 2]]] ``` Segments 0 and 2 lie on `y = x / 2`, and segment 2 lies inside segment 0's extent, so they merge and the point `(4, 2)` is added. Segment 1 lies on the parallel line `y = x / 2 + 1` and is not merged.

Overview: Merge straight lane segments, each given as a list of collinear integer points, so that segments lying on the same line whose extents overlap or touch become one segment containing all of their points. It tests exact collinearity checks, handling of horizontal and vertical segments, and interval merging along each line.

Read the full Applied Intuition Software Engineer interview experience this question came from

Lane geometry is given as a list of straight lane segments. Each segment is a list of integer points `[x, y]` that all lie on one straight line, listed in order along it. Several segments may lie on the same line and overlap. Merge every group of overlapping segments that lie on the same line into one segment containing all of their points, and return the resulting segments. Your function must handle every orientation, including horizontal segments (slope 0) and vertical segments. Implement `merge_lane_segments(segments)`, where `segments[i]` is the list of points of segment `i` and each point is `[x, y]`. Return the output segments in the same format. ### Rules - Two segments lie on the same line when all of their points are collinear. Segments on different lines are never merged, even if they cross, touch or are parallel. - The extent of a segment is the closed stretch of its line from its first point to its last point. Two segments on the same line overlap when their extents share at least one point; touching at a single endpoint counts. - Merging is transitive: if A overlaps B and B overlaps C, all three become one segment, even when A and C do not overlap. - Each output segment, merged or not, is the list of the distinct points of its member segments, sorted by `x` ascending and then by `y` ascending. - Output order: group the output segments by line, and order the lines by the index of the first input segment that lies on each line. Within one line, order the output segments by their first point, by `x` and then by `y`. ### Constraints - `1 <= len(segments) <= 10^4` - `2 <= len(segments[i]) <= 100`, and the total number of points over all segments is at most `10^5`. - Every coordinate is an integer in `[-10^4, 10^4]`. - Within a segment, the points are distinct, collinear, and sorted by `x` ascending and then by `y` ascending. - Different segments may share points or be identical. No input or output value can exceed 2^31 - 1 in magnitude, so 32-bit integers are sufficient in every language. ### Example 1 ```text Input: segments = [[[1, 1], [2, 2], [4, 4]], [[2, 1], [4, 2]], [[3, 3], [6, 6]], [[7, 7], [8, 8]]] Output: [[[1, 1], [2, 2], [3, 3], [4, 4], [6, 6]], [[7, 7], [8, 8]], [[2, 1], [4, 2]]] ``` Segments 0, 2 and 3 lie on the line `y = x`, and segment 1 lies on `y = x / 2`. On `y = x`, segment 0 spans `x` from 1 to 4 and segment 2 spans 3 to 6, so they merge into one segment holding the points of both; segment 3 spans 7 to 8 and stays separate. The line `y = x` holds input segment 0, so its output segments come before the one on `y = x / 2`. ### Example 2 ```text Input: segments = [[[0, 0], [2, 0]], [[1, -2], [1, 0]], [[2, 0], [5, 0]], [[1, 3], [1, 5]], [[6, 0], [7, 0]]] Output: [[[0, 0], [2, 0], [5, 0]], [[6, 0], [7, 0]], [[1, -2], [1, 0]], [[1, 3], [1, 5]]] ``` Segments 0 and 2 lie on the horizontal line `y = 0` and touch at `(2, 0)`, so they merge; segment 4 starts at `x = 6`, after the merged extent ends at `x = 5`, so it stays separate. Segments 1 and 3 lie on the vertical line `x = 1` and do not overlap. Segment 1 ends at `(1, 0)`, a point inside segment 0, but it lies on a different line, so the two are not merged.

Constraints

  • 1 <= len(segments) <= 10^4
  • 2 <= len(segments[i]) <= 100, and the total number of points over all segments is at most 10^5
  • Every coordinate is an integer in [-10^4, 10^4]
  • Within a segment, the points are distinct, collinear, and sorted by x ascending and then by y ascending
  • Different segments may share points or be identical

Examples

Input: ([[[0, 0], [1, 1]]],)

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

Explanation: Minimum valid input: a single two-point segment is returned unchanged.

Input: ([[[1, 1], [2, 2], [4, 4]], [[2, 1], [4, 2]], [[3, 3], [6, 6]], [[7, 7], [8, 8]]],)

Expected Output: [[[1, 1], [2, 2], [3, 3], [4, 4], [6, 6]], [[7, 7], [8, 8]], [[2, 1], [4, 2]]]

Explanation: Source Example 1: on y = x, extents 1..4 and 3..6 merge and 7..8 stays separate; the line y = x / 2 (first segment index 1) comes after y = x (index 0).

Hints

  1. Segments on one line can list points with different spacing, and lines can be horizontal or vertical; look for a description of a line that is identical for all of its segments yet tells parallel lines apart.
  2. Overlap is decided by extents, not by listed points: a segment can lie entirely inside another without sharing any listed point, and touching at one endpoint counts.
  3. Merging is transitive, so one segment can join two groups that do not overlap each other; make sure such groups end up as a single output segment.

Loading coding console...

Show the approach

Approach

Let P be the total number of points. Line identity: every segment's points are sorted by x and then y, so the vector (dx, dy) from its first point to its last point has dx > 0, or dx = 0 and dy > 0. Dividing it by gcd(|dx|, |dy|) gives a reduced direction (a, b) that is the same for every segment on a given line however its points are spaced, and the offset c = ay - bx is constant along that line and differs between parallel lines. The integer triple (a, b, c) therefore identifies the line exactly (|c| <= 4 * 10^8), and horizontal lines (b = 0) and vertical lines (a = 0, b = 1) need no special case. Lines are remembered in the order of their first input segment. Extent: on a non-vertical line x strictly increases along the line, so a segment's extent is the closed interval [x_first, x_last]; on a vertical line it is [y_first, y_last]. Two segments on one line overlap exactly when these closed intervals intersect. Merging: per line, sort the intervals by start and sweep while keeping the running maximum end of the current group; an interval whose start is at most that maximum joins the group, otherwise it opens a new group. Invariant: the current group covers the contiguous interval from its start to the running maximum, and every later interval starts no earlier, so an interval that starts beyond the maximum cannot overlap any member of the group; the sweep therefore yields exactly the transitive overlap components, including endpoint touching and long chains. Output: each group's points are de-duplicated and sorted by (x, y), and groups are emitted in sweep order, which equals the order of their first points because groups on one line have disjoint extents. Edge cases: identical segments and shared points collapse to distinct points, a sparse segment can contain another without sharing a listed point, a one-unit gap does not merge, and crossing, touching or parallel segments on different lines never merge. Sorting dominates the running time.

Time complexity:
O(P log P)
Space complexity:
O(P)