Quick Overview

A coding question that splits a melody's consecutive notes into five-key hand positions whose highest and lowest notes differ by at most four. It asks for the number of hand moves and each note's finger number, testing careful range tracking, correct finger assignment and edge cases such as an empty melody.

Group Consecutive Piano Notes into Hand Positions and Assign Fingers

Company: Google

Role: Machine Learning Engineer

Category: Coding & Algorithms

Difficulty: hard

Interview Round: Technical Screen

A pianist plays a melody with one hand. Notes are integers, where consecutive integers are adjacent keys. The hand's five fingers cover five adjacent keys, so one hand position can play a group of notes only if the highest and lowest notes in the group differ by at most 4. Split the melody into groups of consecutive notes played from one hand position, count how many times the hand has to move, and assign each note a finger. ### Function Signature ```python def assign_fingers(notes: list[int]) -> tuple[int, list[int]]: ``` ### Rules - The notes are played in the given order. The first note starts the first group. - The hand stays in its position as long as it can: a note joins the current group if, with that note included, the largest and smallest notes of the group differ by at most 4. Otherwise the hand moves, the note starts a new group, and the move count increases by 1. - The first hand position is not a move, so `moves` equals the number of groups minus one. - The finger for a note is the note minus the smallest note of its whole group, including notes of the group that are played after it. Finger 0 is the thumb and finger 4 the little finger. - Return `(moves, fingers)`, where `fingers[i]` is the finger for `notes[i]`. For an empty melody return `(0, [])`. ### Constraints - `0 <= len(notes) <= 10^5` - `0 <= notes[i] <= 10^9`; notes may repeat. - All values and differences fit in a 32-bit signed integer. ### Examples **Example 1** ```text Input: notes = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] Output: (1, [4, 3, 2, 1, 0, 4, 3, 2, 1, 0]) ``` The first five notes span `10 - 6 = 4`, so they share one position whose smallest note is 6, giving fingers 4, 3, 2, 1, 0. Adding 5 would make the span `10 - 5 = 5`, so the hand moves once, and 5 through 1 form the second group with smallest note 1. **Example 2** ```text Input: notes = [5, 3, 7, 2, 9] Output: (2, [2, 0, 4, 0, 0]) ``` 5, 3 and 7 span `7 - 3 = 4` and share one position whose smallest note is 3, so note 5 gets finger 2 even though 3 is played after it. Adding 2 would make the span `7 - 2 = 5`, so the hand moves and 2 starts a new group; 9 is 7 above 2, so the hand moves again. **Example 3** ```text Input: notes = [1, 2, 3, 4, 5, 6] Output: (1, [0, 1, 2, 3, 4, 0]) ``` Each step between neighbours is only 1, but 1 through 6 spans 5, so 6 starts a new group.

Overview: A coding question that splits a melody's consecutive notes into five-key hand positions whose highest and lowest notes differ by at most four. It asks for the number of hand moves and each note's finger number, testing careful range tracking, correct finger assignment and edge cases such as an empty melody.

Read the full Google Machine Learning Engineer interview experience this question came from

A pianist plays a melody with one hand. Notes are integers, where consecutive integers are adjacent keys. The hand's five fingers cover five adjacent keys, so one hand position can play a group of notes only if the highest and lowest notes in the group differ by at most 4. Split the melody into groups of consecutive notes played from one hand position, count how many times the hand has to move, and assign each note a finger. Implement `assign_fingers(notes)`. ### Rules - The notes are played in the given order. The first note starts the first group. - The hand stays in its position as long as it can: a note joins the current group if, with that note included, the largest and smallest notes of the group differ by at most 4. Otherwise the hand moves, the note starts a new group, and the move count increases by 1. - The first hand position is not a move, so `moves` equals the number of groups minus one. - The finger for a note is the note minus the smallest note of its whole group, including notes of the group that are played after it. Finger 0 is the thumb and finger 4 the little finger. - Return `(moves, fingers)`, where `fingers[i]` is the finger for `notes[i]`. For an empty melody return `(0, [])`. ### Return value in each language - Python: the tuple `(moves, fingers)`, with `fingers` a list of ints. - JavaScript: the two-element array `[moves, fingers]`. - Java: a `java.util.List<Object>` whose first element is `moves` (an `Integer`) and whose second element is `fingers` (a `java.util.List<Integer>`). - C++: a `std::pair<int, std::vector<int>>` holding `moves` and `fingers`. No value exceeds 2^31 - 1: every note, every difference between notes, `moves` and every finger fit in a 32-bit signed integer, so `int` is enough in Java and C++. ### Example 1 ```text Input: notes = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] Output: (1, [4, 3, 2, 1, 0, 4, 3, 2, 1, 0]) ``` The first five notes span `10 - 6 = 4`, so they share one position whose smallest note is 6, giving fingers 4, 3, 2, 1, 0. Adding 5 would make the span `10 - 5 = 5`, so the hand moves once, and 5 through 1 form the second group with smallest note 1. ### Example 2 ```text Input: notes = [5, 3, 7, 2, 9] Output: (2, [2, 0, 4, 0, 0]) ``` 5, 3 and 7 span `7 - 3 = 4` and share one position whose smallest note is 3, so note 5 gets finger 2 even though 3 is played after it. Adding 2 would make the span `7 - 2 = 5`, so the hand moves and 2 starts a new group; 9 is 7 above 2, so the hand moves again. ### Constraints - `0 <= len(notes) <= 10^5` - `0 <= notes[i] <= 10^9`; notes may repeat. - All values and differences fit in a 32-bit signed integer.

Constraints

  • 0 <= len(notes) <= 10^5
  • 0 <= notes[i] <= 10^9; notes may repeat.
  • All values and differences fit in a 32-bit signed integer.

Examples

Input: ([],)

Expected Output: (0, [])

Explanation: Minimum valid: an empty melody returns (0, []).

Input: ([7],)

Expected Output: (0, [0])

Explanation: Minimum valid: a single note is one group, no move, finger 0.

Hints

  1. Whether a note fits depends on the whole current group, not only on the previous note: a group fits one hand position only while its largest and smallest notes differ by at most 4.
  2. A note's finger is measured from the smallest note of its entire group, and that note may be played later, so a finger is not always known at the moment its note is played.
  3. When a note does not fit, it begins the next group. Check the boundary: a span of exactly 4 still fits, a span of 5 does not.

Loading coding console...

Show the approach

Approach

Scan the notes once, keeping the index where the current group started and the smallest (lo) and largest (hi) note of that group. For each next note x, compute the span the group would have with x included, max(hi, x) - min(lo, x). If that span is at most 4, x joins the group and lo/hi are updated. Otherwise the current group is closed: every note from its start index up to the previous note gets finger = note - lo, the move count increases by 1, and x starts a fresh group with lo = hi = x. After the scan the last open group is closed the same way.

Invariant: before note i is processed, notes[start..i-1] is exactly the current group produced by the rules, and lo/hi are its minimum and maximum. The join test uses the whole-group span with the new note included, which is exactly the rule, so every join/move decision matches it. Fingers are written only when a group is closed, when its minimum is final, so a note played before its group's minimum still gets the right finger. Each group except the first adds exactly one move, so moves equals the number of groups minus one.

Edge cases: an empty melody returns (0, []); a single note returns (0, [0]); a span of exactly 4 stays in the group while a span of 5 forces a move; the span is measured over the whole group, not between neighbouring notes; the note that triggers a move starts the new group rather than joining the old one. Notes up to 10^9 and their differences fit in a 32-bit int, and every finger is between 0 and 4.

Time complexity:
O(n)
Space complexity:
O(n)