Merge Call-Recording Labels from Multiple Labelers by Majority Vote

Read the full interview experience this question came from →

Quick Overview

Given several labelers' segmentations of a 100-second call recording into human, machine and silence stretches, build one consensus timeline in which each label needs strictly more than half of the votes. Tests sweeping over unaligned interval boundaries, merging adjacent equal labels and detecting stretches with no majority.

Merge Call-Recording Labels from Multiple Labelers by Majority Vote

Company: Nooks

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Technical Screen

A team labels phone-call recordings. Every recording is exactly 100 seconds long, and each of `N` labelers has independently split the same recording into segments, giving every segment one of only three labels: `human`, `machine` or `silence`. Combine the labelers' work into a single labeling of the recording by consensus: at every moment, the final label is the one that more than half of the labelers gave to that moment. The result must also cover the full 100 seconds. In the original problem, a stretch of time on which no label reaches consensus raises an error; in this console version the function returns an empty list instead. ### Function Signature ```python def consensus_labels(labelers: list[list[list[str]]]) -> list[list[str]]: ``` ### Rules - `labelers[i]` is labeler `i`'s segmentation: a list of segments `[start, end, label]`, where `start` and `end` are times in seconds written as decimal strings (for example `"10.5"`) and `label` is `"human"`, `"machine"` or `"silence"`. A segment covers the half-open interval `[start, end)`. - Times are compared by numeric value, not as strings. - At a time `t`, labeler `i` votes for the label of its segment that contains `t`. A label has consensus at `t` when strictly more than `N / 2` labelers vote for it, where `N = len(labelers)`. For `N = 3` that means at least 2 votes; for `N = 4`, at least 3. - Return a list of segments `[start, end, label]` in increasing time order that covers `[0.0, 100.0)` with no gaps or overlaps, such that at every time the covering segment's label is the consensus label at that time, and no two adjacent segments have the same label (adjacent stretches with the same consensus label form one segment). - Write every time in the result exactly as it is spelled in the input. Each time value has only one spelling (see Constraints), so the result is unique. - If there is any stretch of positive length on which no label has consensus, return `[]` instead. A successful result is never empty, so `[]` stands in for the error the original problem raises. ### Constraints - `1 <= N = len(labelers) <= 100` - `1 <= len(labelers[i]) <= 1000` for every `i` - Every time string has an integer part with no leading zeros (a lone `"0"` is allowed), a decimal point, and exactly one digit after it, such as `"0.0"`, `"9.0"`, `"10.5"` or `"100.0"`. Every value lies in `[0.0, 100.0]`. - Within one labeler's list, segments are in increasing time order: the first starts at `"0.0"`, the last ends at `"100.0"`, each segment starts exactly where the previous one ends, and every segment has `start < end`. - Two consecutive segments of the same labeler may carry the same label. - Labels are exactly `"human"`, `"machine"` or `"silence"` (lowercase). ### Examples **Example 1** ```text Input: labelers = [ [["0.0", "10.5", "human"], ["10.5", "20.0", "machine"], ["20.0", "100.0", "silence"]], [["0.0", "9.0", "human"], ["9.0", "20.0", "silence"], ["20.0", "100.0", "human"]], [["0.0", "20.0", "silence"], ["20.0", "100.0", "human"]] ] Output: [["0.0", "9.0", "human"], ["9.0", "20.0", "silence"], ["20.0", "100.0", "human"]] ``` With `N = 3`, a label needs at least 2 votes. On `[0.0, 9.0)` the votes are human, human, silence. On `[9.0, 10.5)` they are human, silence, silence, and on `[10.5, 20.0)` machine, silence, silence, so silence holds all of `[9.0, 20.0)` as one segment. On `[20.0, 100.0)` they are silence, human, human. **Example 2** ```text Input: labelers = [ [["0.0", "50.0", "human"], ["50.0", "100.0", "silence"]], [["0.0", "22.5", "human"], ["22.5", "47.5", "human"], ["47.5", "100.0", "silence"]], [["0.0", "50.0", "human"], ["50.0", "100.0", "machine"]], [["0.0", "8.5", "machine"], ["8.5", "50.0", "human"], ["50.0", "100.0", "silence"]] ] Output: [["0.0", "50.0", "human"], ["50.0", "100.0", "silence"]] ``` With `N = 4`, a label needs at least 3 votes. The votes are human, human, human, machine on `[0.0, 8.5)`; all four human on `[8.5, 47.5)`; human, silence, human, human on `[47.5, 50.0)`; and silence, silence, machine, silence on `[50.0, 100.0)`. **Example 3** ```text Input: labelers = [ [["0.0", "40.0", "human"], ["40.0", "100.0", "machine"]], [["0.0", "60.0", "human"], ["60.0", "100.0", "silence"]], [["0.0", "50.0", "silence"], ["50.0", "100.0", "machine"]] ] Output: [] ``` On `[40.0, 50.0)` the votes are machine, human, silence, so no label reaches 2 votes. Every other stretch has consensus, but one stretch without it is enough to return `[]`.

Overview: Given several labelers' segmentations of a 100-second call recording into human, machine and silence stretches, build one consensus timeline in which each label needs strictly more than half of the votes. Tests sweeping over unaligned interval boundaries, merging adjacent equal labels and detecting stretches with no majority.

Read the full Nooks Software Engineer interview experience this question came from

Nooks logo
Nooks
Oct 15, 2026
mediumSoftware EngineerTechnical ScreenCoding & Algorithms
0
0

A team labels phone-call recordings. Every recording is exactly 100 seconds long, and each of N labelers has independently split the same recording into segments, giving every segment one of only three labels: human, machine or silence. Combine the labelers' work into a single labeling of the recording by consensus: at every moment, the final label is the one that more than half of the labelers gave to that moment. The result must also cover the full 100 seconds. In the original problem, a stretch of time on which no label reaches consensus raises an error; in this console version the function returns an empty list instead.

Function Signature

def consensus_labels(labelers: list[list[list[str]]]) -> list[list[str]]:

Rules

  • labelers[i] is labeler i 's segmentation: a list of segments [start, end, label] , where start and end are times in seconds written as decimal strings (for example "10.5" ) and label is "human" , "machine" or "silence" . A segment covers the half-open interval [start, end) .
  • Times are compared by numeric value, not as strings.
  • At a time t , labeler i votes for the label of its segment that contains t . A label has consensus at t when strictly more than N / 2 labelers vote for it, where N = len(labelers) . For N = 3 that means at least 2 votes; for N = 4 , at least 3.
  • Return a list of segments [start, end, label] in increasing time order that covers [0.0, 100.0) with no gaps or overlaps, such that at every time the covering segment's label is the consensus label at that time, and no two adjacent segments have the same label (adjacent stretches with the same consensus label form one segment).
  • Write every time in the result exactly as it is spelled in the input. Each time value has only one spelling (see Constraints), so the result is unique.
  • If there is any stretch of positive length on which no label has consensus, return [] instead. A successful result is never empty, so [] stands in for the error the original problem raises.

Constraints

  • 1 <= N = len(labelers) <= 100
  • 1 <= len(labelers[i]) <= 1000 for every i
  • Every time string has an integer part with no leading zeros (a lone "0" is allowed), a decimal point, and exactly one digit after it, such as "0.0" , "9.0" , "10.5" or "100.0" . Every value lies in [0.0, 100.0] .
  • Within one labeler's list, segments are in increasing time order: the first starts at "0.0" , the last ends at "100.0" , each segment starts exactly where the previous one ends, and every segment has start < end .
  • Two consecutive segments of the same labeler may carry the same label.
  • Labels are exactly "human" , "machine" or "silence" (lowercase).

Examples

Example 1

Input:  labelers = [
          [["0.0", "10.5", "human"], ["10.5", "20.0", "machine"], ["20.0", "100.0", "silence"]],
          [["0.0", "9.0", "human"], ["9.0", "20.0", "silence"], ["20.0", "100.0", "human"]],
          [["0.0", "20.0", "silence"], ["20.0", "100.0", "human"]]
        ]
Output: [["0.0", "9.0", "human"], ["9.0", "20.0", "silence"], ["20.0", "100.0", "human"]]

With N = 3, a label needs at least 2 votes. On [0.0, 9.0) the votes are human, human, silence. On [9.0, 10.5) they are human, silence, silence, and on [10.5, 20.0) machine, silence, silence, so silence holds all of [9.0, 20.0) as one segment. On [20.0, 100.0) they are silence, human, human.

Example 2

Input:  labelers = [
          [["0.0", "50.0", "human"], ["50.0", "100.0", "silence"]],
          [["0.0", "22.5", "human"], ["22.5", "47.5", "human"], ["47.5", "100.0", "silence"]],
          [["0.0", "50.0", "human"], ["50.0", "100.0", "machine"]],
          [["0.0", "8.5", "machine"], ["8.5", "50.0", "human"], ["50.0", "100.0", "silence"]]
        ]
Output: [["0.0", "50.0", "human"], ["50.0", "100.0", "silence"]]

With N = 4, a label needs at least 3 votes. The votes are human, human, human, machine on [0.0, 8.5); all four human on [8.5, 47.5); human, silence, human, human on [47.5, 50.0); and silence, silence, machine, silence on [50.0, 100.0).

Example 3

Input:  labelers = [
          [["0.0", "40.0", "human"], ["40.0", "100.0", "machine"]],
          [["0.0", "60.0", "human"], ["60.0", "100.0", "silence"]],
          [["0.0", "50.0", "silence"], ["50.0", "100.0", "machine"]]
        ]
Output: []

On [40.0, 50.0) the votes are machine, human, silence, so no label reaches 2 votes. Every other stretch has consensus, but one stretch without it is enough to return [].