Facebook Pixel

1629. Slowest Key

Problem Description

You're given data about a keypad testing session where a tester pressed n keys in sequence. The problem provides two pieces of information:

  1. keysPressed: A string of length n where keysPressed[i] represents the character of the i-th key pressed
  2. releaseTimes: An array of length n where releaseTimes[i] represents the time when the i-th key was released

The testing follows these rules:

  • The first key (index 0) was pressed at time 0
  • Each subsequent key was pressed exactly when the previous key was released
  • The duration of the i-th keypress is calculated as releaseTimes[i] - releaseTimes[i-1] for i > 0
  • The duration of the first keypress (index 0) is simply releaseTimes[0]

Your task is to find which key had the longest duration press. If multiple keys have the same longest duration, return the lexicographically largest key among them.

For example, if keys 'a' and 'c' both have the longest duration of 5 time units, you would return 'c' since it comes after 'a' alphabetically.

The solution iterates through each keypress, calculates its duration, and keeps track of the key with the longest duration. When encountering a tie in duration, it selects the lexicographically larger key by comparing their ASCII values using ord().

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is that we need to track two things as we examine each keypress: the maximum duration seen so far and the key associated with that duration.

Since we want to find the keypress with the longest duration, we naturally think of a linear scan through all keypresses, calculating each duration and comparing it with our current maximum. This is a classic "find the maximum" pattern.

For calculating durations, we observe that:

  • The first key's duration is simply releaseTimes[0] (since it was pressed at time 0)
  • Every other key's duration is the difference between its release time and the previous key's release time: releaseTimes[i] - releaseTimes[i-1]

The twist in this problem is the tiebreaker rule: when multiple keys have the same maximum duration, we need the lexicographically largest one. This means when we encounter a duration equal to our current maximum, we can't just ignore it - we need to check if the current key is lexicographically larger than our stored answer.

This leads to our update condition having two parts:

  1. Update if the current duration is strictly greater than the maximum (d > mx)
  2. Update if the current duration equals the maximum AND the current key is lexicographically larger (d == mx and ord(keysPressed[i]) > ord(ans))

By maintaining just two variables (ans for the key and mx for the maximum duration) and updating them according to these rules during a single pass through the data, we efficiently find our answer in O(n) time with O(1) extra space.

Solution Approach

The solution uses a single-pass linear scan algorithm with two tracking variables to find the key with the longest duration.

Implementation Steps:

  1. Initialize tracking variables:

    • ans = keysPressed[0]: Store the first key as the initial answer
    • mx = releaseTimes[0]: Store the first key's duration as the initial maximum
  2. Iterate through remaining keypresses (starting from index 1):

    for i in range(1, len(keysPressed)):
  3. Calculate duration for current keypress:

    d = releaseTimes[i] - releaseTimes[i - 1]

    This gives us the time difference between the current key's release and the previous key's release.

  4. Update maximum and answer based on comparison:

    if d > mx or (d == mx and ord(keysPressed[i]) > ord(ans)):
        mx = d
        ans = keysPressed[i]

    The update happens in two cases:

    • When current duration d is strictly greater than mx
    • When current duration equals mx but the current key is lexicographically larger (checked using ord() to compare ASCII values)
  5. Return the result: After examining all keypresses, ans contains the key with the longest duration (with lexicographic tiebreaking applied).

Time Complexity: O(n) where n is the number of keypresses - we make a single pass through the arrays.

Space Complexity: O(1) - we only use a constant amount of extra space for the tracking variables ans, mx, and d.

The algorithm efficiently solves the problem by maintaining a running maximum and making decisions locally at each step, eliminating the need for sorting or additional data structures.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a concrete example to illustrate the solution approach.

Input:

  • keysPressed = "cbad"
  • releaseTimes = [4, 10, 16, 20]

Step-by-step execution:

Initial Setup:

  • ans = 'c' (first key)
  • mx = 4 (first key's duration: 4 - 0 = 4)

Iteration 1 (i=1, key='b'):

  • Calculate duration: d = 10 - 4 = 6
  • Compare: 6 > 4 (d > mx)? Yes!
  • Update: mx = 6, ans = 'b'

Iteration 2 (i=2, key='a'):

  • Calculate duration: d = 16 - 10 = 6
  • Compare: 6 > 6 (d > mx)? No
  • Compare: 6 == 6 (d == mx)? Yes, check lexicographic order
  • Is 'a' > 'b'? No (ord('a')=97 < ord('b')=98)
  • No update: mx = 6, ans = 'b'

Iteration 3 (i=3, key='d'):

  • Calculate duration: d = 20 - 16 = 4
  • Compare: 4 > 6 (d > mx)? No
  • Compare: 4 == 6 (d == mx)? No
  • No update: mx = 6, ans = 'b'

Result: Return 'b'

The key 'b' had the longest duration of 6 time units. Even though 'a' also had a duration of 6, we kept 'b' because it's lexicographically larger than 'a'.

Solution Implementation

1class Solution:
2    def slowestKey(self, releaseTimes: List[int], keysPressed: str) -> str:
3        # Initialize the answer with the first key pressed
4        slowest_key = keysPressed[0]
5        # Initialize the maximum duration with the first key's release time
6        max_duration = releaseTimes[0]
7      
8        # Iterate through the remaining keys starting from index 1
9        for i in range(1, len(keysPressed)):
10            # Calculate the duration for the current key press
11            # Duration = current release time - previous release time
12            current_duration = releaseTimes[i] - releaseTimes[i - 1]
13          
14            # Update the slowest key if:
15            # 1. Current duration is strictly greater than max duration, OR
16            # 2. Current duration equals max duration AND current key is lexicographically larger
17            if current_duration > max_duration or (current_duration == max_duration and ord(keysPressed[i]) > ord(slowest_key)):
18                max_duration = current_duration
19                slowest_key = keysPressed[i]
20      
21        # Return the key with the longest duration (or lexicographically largest if tied)
22        return slowest_key
23
1class Solution {
2    public char slowestKey(int[] releaseTimes, String keysPressed) {
3        // Initialize the result with the first key pressed
4        char slowestKey = keysPressed.charAt(0);
5        // Initialize the maximum duration with the first key's press time
6        int maxDuration = releaseTimes[0];
7      
8        // Iterate through the remaining keys starting from index 1
9        for (int i = 1; i < releaseTimes.length; i++) {
10            // Calculate the duration for the current key press
11            // Duration = current release time - previous release time
12            int currentDuration = releaseTimes[i] - releaseTimes[i - 1];
13          
14            // Update the slowest key if:
15            // 1. Current duration is greater than the maximum duration, OR
16            // 2. Current duration equals maximum AND current key is lexicographically larger
17            if (currentDuration > maxDuration || 
18                (currentDuration == maxDuration && keysPressed.charAt(i) > slowestKey)) {
19                maxDuration = currentDuration;
20                slowestKey = keysPressed.charAt(i);
21            }
22        }
23      
24        // Return the key with the longest duration (or lexicographically largest if tied)
25        return slowestKey;
26    }
27}
28
1class Solution {
2public:
3    char slowestKey(vector<int>& releaseTimes, string keysPressed) {
4        // Initialize the result with the first key pressed
5        char slowestKey = keysPressed[0];
6        // Initialize the maximum duration with the first key's press time
7        int maxDuration = releaseTimes[0];
8      
9        // Iterate through all keys starting from the second one
10        for (int i = 1; i < releaseTimes.size(); ++i) {
11            // Calculate the duration of current key press
12            // Duration = current release time - previous release time
13            int currentDuration = releaseTimes[i] - releaseTimes[i - 1];
14          
15            // Update the slowest key if:
16            // 1. Current duration is greater than max duration, OR
17            // 2. Current duration equals max duration AND current key is lexicographically larger
18            if (currentDuration > maxDuration || 
19                (currentDuration == maxDuration && keysPressed[i] > slowestKey)) {
20                maxDuration = currentDuration;
21                slowestKey = keysPressed[i];
22            }
23        }
24      
25        // Return the key with the longest duration (or lexicographically largest if tied)
26        return slowestKey;
27    }
28};
29
1function slowestKey(releaseTimes: number[], keysPressed: string): string {
2    // Initialize the result with the first key pressed
3    let slowestKey: string = keysPressed[0];
4    // Initialize the maximum duration with the first key's press time
5    let maxDuration: number = releaseTimes[0];
6  
7    // Iterate through all keys starting from the second one
8    for (let i = 1; i < releaseTimes.length; i++) {
9        // Calculate the duration of current key press
10        // Duration = current release time - previous release time
11        const currentDuration: number = releaseTimes[i] - releaseTimes[i - 1];
12      
13        // Update the slowest key if:
14        // 1. Current duration is greater than max duration, OR
15        // 2. Current duration equals max duration AND current key is lexicographically larger
16        if (currentDuration > maxDuration || 
17            (currentDuration === maxDuration && keysPressed[i] > slowestKey)) {
18            maxDuration = currentDuration;
19            slowestKey = keysPressed[i];
20        }
21    }
22  
23    // Return the key with the longest duration (or lexicographically largest if tied)
24    return slowestKey;
25}
26

Time and Space Complexity

Time Complexity: O(n) where n is the length of the keysPressed string (which is also the length of releaseTimes array).

The algorithm iterates through the arrays exactly once using a single for loop from index 1 to n-1. Within each iteration, it performs constant time operations:

  • Array access: releaseTimes[i] and releaseTimes[i-1] - O(1)
  • Subtraction operation to calculate duration d - O(1)
  • Comparison operations and character comparison using ord() - O(1)
  • Variable assignments - O(1)

Since we perform n-1 iterations with O(1) work per iteration, the total time complexity is O(n).

Space Complexity: O(1) - constant space.

The algorithm uses only a fixed amount of extra space regardless of input size:

  • ans: stores a single character
  • mx: stores a single integer for the maximum duration
  • d: stores a single integer for the current duration
  • i: loop counter variable

No additional data structures like arrays, lists, or hashmaps are created that scale with the input size. The space used remains constant throughout the execution.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Forgetting to Handle the First Key's Duration Correctly

A common mistake is treating the first key's duration as 0 or ignoring it entirely when initializing variables. Some developers might start their iteration from index 0 instead of 1, leading to incorrect duration calculations.

Incorrect approach:

# Wrong: Starting with default values
slowest_key = ''
max_duration = 0
for i in range(len(keysPressed)):  # Wrong: should start from 1
    duration = releaseTimes[i] - releaseTimes[i-1]  # Error when i=0

Correct approach:

# Initialize with first key's data
slowest_key = keysPressed[0]
max_duration = releaseTimes[0]
for i in range(1, len(keysPressed)):  # Start from index 1
    duration = releaseTimes[i] - releaseTimes[i-1]

2. Incorrect Lexicographic Comparison

Another pitfall is comparing characters incorrectly when handling ties. Some might compare in the wrong direction or forget to handle the tie case at all.

Incorrect approaches:

# Wrong: Using less than instead of greater than
if d == mx and ord(keysPressed[i]) < ord(ans):  # Wrong direction

# Wrong: Using string comparison without considering single characters
if d == mx and keysPressed[i] < ans:  # Works but less explicit

# Wrong: Forgetting the tie-breaking condition entirely
if d > mx:  # Missing the lexicographic comparison for ties

Correct approach:

# Use greater than for lexicographically larger character
if d > mx or (d == mx and ord(keysPressed[i]) > ord(ans)):
    mx = d
    ans = keysPressed[i]

3. Off-by-One Errors in Duration Calculation

Some developers might incorrectly calculate durations by using the wrong indices or forgetting that each key is pressed when the previous one is released.

Incorrect approach:

# Wrong: Using wrong index relationship
duration = releaseTimes[i+1] - releaseTimes[i]  # Off by one

# Wrong: Subtracting from 0 for all keys
duration = releaseTimes[i] - 0  # Incorrect for i > 0

Correct approach:

# For first key (i=0): duration = releaseTimes[0] - 0 = releaseTimes[0]
# For other keys: duration = releaseTimes[i] - releaseTimes[i-1]
if i == 0:
    duration = releaseTimes[0]
else:
    duration = releaseTimes[i] - releaseTimes[i-1]

4. Not Understanding the Problem's Timing Model

A conceptual pitfall is misunderstanding how the keypress timing works. Each key is pressed exactly when the previous key is released, creating a continuous sequence with no gaps or overlaps.

Prevention: Visualize the timeline:

  • Time 0: Key 0 pressed
  • Time releaseTimes[0]: Key 0 released, Key 1 pressed
  • Time releaseTimes[1]: Key 1 released, Key 2 pressed
  • And so on...

This mental model helps avoid confusion about duration calculations and ensures correct implementation of the algorithm.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What data structure does Breadth-first search typically uses to store intermediate states?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More