Facebook Pixel

681. Next Closest Time 🔒

MediumHash TableStringBacktrackingEnumeration
Leetcode Link

Problem Description

You are given a time in 24-hour format as a string "HH:MM" (like "19:34" or "23:59"). Your task is to find the next closest time that can be formed by reusing only the digits that appear in the given time. You can use any digit from the original time as many times as you want.

For example:

  • If the input is "19:34", you can only use the digits 1, 9, 3, and 4 to form the next time
  • Each digit can be reused multiple times - you could use 1 four times to make "11:11" if that's valid

The next closest time means the earliest valid time that comes after the given time. If no such time exists on the same day (within 24 hours), you should wrap around to the next day and find the earliest possible time using the available digits.

Key constraints:

  • The input time string is always valid (proper format with two digits for hours and minutes)
  • The output must be a valid 24-hour time (hours from 00 to 23, minutes from 00 to 59)
  • You must use only the digits present in the original time, but can repeat them as needed

For instance:

  • Input: "19:34" might return "19:39" (using digits 1, 9, 3, 4)
  • Input: "23:59" might return "22:22" (wrapping to next day's earliest time with digits 2, 3, 5, 9)

Flowchart Walkthrough

First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:

Is it a graph?

  • No: The problem doesn't involve nodes and edges or graph traversal. We're working with time strings and digit manipulation.

Need to solve for kth smallest/largest?

  • No: We're not looking for the kth element in any ordering. We need the very next valid time after the given time.

Involves Linked Lists?

  • No: The problem deals with strings representing time, not linked list data structures.

Does the problem have small constraints?

  • Yes: We have very small constraints here. We only have at most 4 unique digits (from the time string), and we need to generate all possible 4-digit combinations using these digits. The maximum number of combinations is 4^4 = 256, which is quite small.

Brute force / Backtracking?

  • Yes: Given the small constraints, we can use backtracking to generate all possible 4-digit combinations using the available digits, check if each forms a valid time, and find the next closest one.

Conclusion: The flowchart correctly leads us to use a Backtracking approach. The solution generates all possible 4-digit combinations from the available digits in the input time, validates each combination as a valid time format, and finds the minimum valid time that comes after the given time (with wraparound handling for the next day).

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

Intuition

The key insight is recognizing that we have a very limited set of digits to work with - only the digits that appear in the original time. Since a time string has only 4 digit positions (HH:MM), and we can only use the existing digits, the total number of possible combinations is quite small.

Think about it this way: if the time is "19:34", we can only use digits 1, 9, 3, and 4. Even if we try every possible 4-digit combination using these digits, we'd have at most 4^4 = 256 possibilities to check. This is small enough to examine exhaustively.

The brute force approach becomes elegant here:

  1. Extract all unique digits from the input time (ignoring the colon)
  2. Generate all possible 4-digit combinations using these digits
  3. For each combination, check if it forms a valid time (hours < 24, minutes < 60)
  4. Among all valid times, find the one that's closest to but after our current time

The tricky part is handling the "next" time. We convert times to minutes since midnight for easy comparison. If the current time is 19:34, that's 19 * 60 + 34 = 1174 minutes. Any valid time with more minutes but less than 1440 (24 hours) comes after it on the same day.

What if no valid time exists after the current time using our digits? This happens when we're near the end of the day with large digits. In this case, we need to wrap around to the next day and find the earliest possible time. The earliest time is simply formed by using the smallest available digit in all four positions.

For example, if the input is "23:59" and we have digits 2, 3, 5, 9, the next time on the same day would need to start with 23: or higher, but 23:92, 23:93, etc. are invalid. So we wrap around to the next day and use the smallest digit 2 to form "22:22".

This backtracking approach naturally explores all possibilities and finds the optimal answer without missing any edge cases.

Learn more about Backtracking patterns.

Solution Approach

The implementation uses a depth-first search (DFS) with backtracking to generate all possible 4-digit combinations and find the next closest valid time.

1. Extract Available Digits: First, we extract all unique digits from the input time string, excluding the colon:

s = {c for c in time if c != ':'}

This gives us the set of digits we can use to form new times.

2. Convert Current Time to Minutes: We convert the input time to total minutes since midnight for easier comparison:

t = int(time[:2]) * 60 + int(time[3:])

For example, "19:34" becomes 19 * 60 + 34 = 1174 minutes.

3. DFS/Backtracking to Generate Combinations: The dfs function recursively builds 4-digit strings:

def dfs(curr):
    if len(curr) == 4:
        # Process complete 4-digit combination
        ...
    for c in s:
        dfs(curr + c)

When we have a complete 4-digit combination, we:

  • Check if it forms a valid time using the check function
  • Convert it to minutes: p = int(curr[:2]) * 60 + int(curr[2:])
  • If it's valid and comes after our current time but is closer than any previously found time, update our answer

4. Validity Check: The check function ensures the 4-digit string represents a valid time:

def check(t):
    h, m = int(t[:2]), int(t[2:])
    return 0 <= h < 24 and 0 <= m < 60

5. Finding the Closest Next Time: We track the minimum difference d between candidate times and the current time:

if t < p < t + d:
    d = p - t
    ans = curr[:2] + ':' + curr[2:]

We only consider times p that are:

  • Greater than the current time t (comes after)
  • Less than t + d (closer than any previously found time)

6. Handling Wraparound: If no valid time is found after the current time (when ans remains None), we need the earliest time on the next day. This is formed by using the smallest digit in all positions:

if ans is None:
    mi = min(int(c) for c in s)
    ans = f'{mi}{mi}:{mi}{mi}'

Time Complexity: O(4^4) = O(256) at most, since we have at most 4 unique digits and need to fill 4 positions.

Space Complexity: O(4) for the recursion stack depth and storing the digit set.

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 trace through the solution with input "19:34".

Step 1: Extract digits

  • Available digits: {1, 9, 3, 4}
  • Current time in minutes: 19 * 60 + 34 = 1174

Step 2: Generate and check combinations via DFS

The DFS explores all 4-digit combinations. Here are some key combinations it generates:

  • "1111" → 11:11 (671 minutes) - Valid, but 671 < 1174 (earlier than current)
  • "1934" → 19:34 (1174 minutes) - Valid, but equals current time (not after)
  • "1939" → 19:39 (1179 minutes) - Valid! 1179 > 1174 (after current)
    • Difference: 1179 - 1174 = 5 minutes
    • Update: ans = "19:39", d = 5
  • "1941" → 19:41 (1181 minutes) - Valid, 1181 > 1174
    • Difference: 1181 - 1174 = 7 minutes
    • Since 7 > 5, don't update (not closer)
  • "1943" → 19:43 (1183 minutes) - Valid, but difference is 9 > 5
  • "1944" → 19:44 (1184 minutes) - Valid, but difference is 10 > 5
  • "1991" → 19:91 - Invalid! (91 minutes > 59)
  • "3333" → 33:33 - Invalid! (33 hours > 23)

Step 3: Result After checking all combinations, the closest valid time after 19:34 is 19:39.


Let's also trace a wraparound example with input "23:59".

Step 1: Extract digits

  • Available digits: {2, 3, 5, 9}
  • Current time in minutes: 23 * 60 + 59 = 1439

Step 2: Generate and check combinations

Late-day combinations:

  • "2359" → 23:59 (1439 minutes) - Equals current (not after)
  • "2392" → 23:92 - Invalid! (92 minutes > 59)
  • "2395" → 23:95 - Invalid!
  • "2399" → 23:99 - Invalid!
  • "2522" → 25:22 - Invalid! (25 hours > 23)
  • "9999" → 99:99 - Invalid!

No valid time exists after 23:59 using digits {2, 3, 5, 9}.

Step 3: Wraparound to next day Since ans is still None, find the earliest possible time:

  • Minimum digit: 2
  • Form: "22:22"

Result: "22:22" (the earliest time on the next day using available digits)

Solution Implementation

1class Solution:
2    def nextClosestTime(self, time: str) -> str:
3        def is_valid_time(time_str):
4            """Check if the given 4-digit string represents a valid time."""
5            hours = int(time_str[:2])
6            minutes = int(time_str[2:])
7            return 0 <= hours < 24 and 0 <= minutes < 60
8
9        def dfs(current_combination):
10            """Recursively build 4-digit combinations and find the closest next time."""
11            # Base case: when we have a 4-digit combination
12            if len(current_combination) == 4:
13                # Skip invalid time combinations
14                if not is_valid_time(current_combination):
15                    return
16              
17                # Convert current combination to minutes for comparison
18                nonlocal closest_time, min_difference
19                current_minutes = int(current_combination[:2]) * 60 + int(current_combination[2:])
20              
21                # Check if this is a valid next time (after current but closer than previous best)
22                if original_minutes < current_minutes < original_minutes + min_difference:
23                    min_difference = current_minutes - original_minutes
24                    closest_time = current_combination[:2] + ':' + current_combination[2:]
25                return
26          
27            # Try adding each available digit to the current combination
28            for digit in available_digits:
29                dfs(current_combination + digit)
30
31        # Extract unique digits from the input time (excluding colon)
32        available_digits = {char for char in time if char != ':'}
33      
34        # Convert original time to minutes since midnight for easier comparison
35        original_minutes = int(time[:2]) * 60 + int(time[3:])
36      
37        # Initialize variables to track the closest next time
38        min_difference = float('inf')
39        closest_time = None
40      
41        # Start DFS to find all possible combinations
42        dfs('')
43      
44        # If no valid next time found today, return the earliest possible time tomorrow
45        # using the smallest available digit
46        if closest_time is None:
47            smallest_digit = min(int(digit) for digit in available_digits)
48            closest_time = f'{smallest_digit}{smallest_digit}:{smallest_digit}{smallest_digit}'
49      
50        return closest_time
51
1class Solution {
2    private int currentTimeInMinutes;
3    private int minTimeDifference;
4    private String result;
5    private Set<Character> availableDigits;
6
7    /**
8     * Find the next closest time using only the digits from the given time
9     * @param time - input time in "HH:MM" format
10     * @return the next closest time using same digits
11     */
12    public String nextClosestTime(String time) {
13        // Convert current time to minutes for easier comparison
14        currentTimeInMinutes = Integer.parseInt(time.substring(0, 2)) * 60 
15                              + Integer.parseInt(time.substring(3));
16      
17        // Initialize minimum difference to find closest time
18        minTimeDifference = Integer.MAX_VALUE;
19      
20        // Collect all unique digits from the input time
21        availableDigits = new HashSet<>();
22        char smallestDigit = 'z';
23        for (char c : time.toCharArray()) {
24            if (c != ':') {
25                availableDigits.add(c);
26                if (c < smallestDigit) {
27                    smallestDigit = c;
28                }
29            }
30        }
31      
32        // Initialize result as null
33        result = null;
34      
35        // Try all possible combinations of 4 digits
36        generateTimeCombinations("");
37      
38        // If no valid next time found, return earliest possible time using smallest digit
39        if (result == null) {
40            result = "" + smallestDigit + smallestDigit + ":" + smallestDigit + smallestDigit;
41        }
42      
43        return result;
44    }
45
46    /**
47     * Recursively generate all possible 4-digit combinations and find the closest valid time
48     * @param currentCombination - current partial combination being built
49     */
50    private void generateTimeCombinations(String currentCombination) {
51        // Base case: when we have 4 digits
52        if (currentCombination.length() == 4) {
53            // Check if this forms a valid time
54            if (!isValidTime(currentCombination)) {
55                return;
56            }
57          
58            // Convert to minutes and check if it's the next closest time
59            int candidateTimeInMinutes = Integer.parseInt(currentCombination.substring(0, 2)) * 60 
60                                        + Integer.parseInt(currentCombination.substring(2));
61          
62            // Update result if this time is later than current and closer than previous candidates
63            if (candidateTimeInMinutes > currentTimeInMinutes 
64                && candidateTimeInMinutes - currentTimeInMinutes < minTimeDifference) {
65                minTimeDifference = candidateTimeInMinutes - currentTimeInMinutes;
66                result = currentCombination.substring(0, 2) + ":" + currentCombination.substring(2);
67            }
68            return;
69        }
70      
71        // Try adding each available digit to current combination
72        for (char digit : availableDigits) {
73            generateTimeCombinations(currentCombination + digit);
74        }
75    }
76
77    /**
78     * Check if a 4-digit string represents a valid time
79     * @param timeString - 4-digit string representing HHMM
80     * @return true if valid time, false otherwise
81     */
82    private boolean isValidTime(String timeString) {
83        int hours = Integer.parseInt(timeString.substring(0, 2));
84        int minutes = Integer.parseInt(timeString.substring(2));
85      
86        // Valid time has hours 0-23 and minutes 0-59
87        return hours >= 0 && hours < 24 && minutes >= 0 && minutes < 60;
88    }
89}
90
1class Solution {
2private:
3    int currentTimeInMinutes;
4    int minTimeDifference;
5    string result;
6    unordered_set<char> availableDigits;
7  
8    /**
9     * Recursively generate all possible 4-digit combinations and find the closest valid time
10     * @param currentCombination - current partial combination being built
11     */
12    void generateTimeCombinations(string currentCombination) {
13        // Base case: when we have 4 digits
14        if (currentCombination.length() == 4) {
15            // Check if this forms a valid time
16            if (!isValidTime(currentCombination)) {
17                return;
18            }
19          
20            // Convert to minutes and check if it's the next closest time
21            int candidateTimeInMinutes = stoi(currentCombination.substr(0, 2)) * 60 
22                                        + stoi(currentCombination.substr(2));
23          
24            // Update result if this time is later than current and closer than previous candidates
25            if (candidateTimeInMinutes > currentTimeInMinutes 
26                && candidateTimeInMinutes - currentTimeInMinutes < minTimeDifference) {
27                minTimeDifference = candidateTimeInMinutes - currentTimeInMinutes;
28                result = currentCombination.substr(0, 2) + ":" + currentCombination.substr(2);
29            }
30            return;
31        }
32      
33        // Try adding each available digit to current combination
34        for (char digit : availableDigits) {
35            generateTimeCombinations(currentCombination + digit);
36        }
37    }
38  
39    /**
40     * Check if a 4-digit string represents a valid time
41     * @param timeString - 4-digit string representing HHMM
42     * @return true if valid time, false otherwise
43     */
44    bool isValidTime(const string& timeString) {
45        int hours = stoi(timeString.substr(0, 2));
46        int minutes = stoi(timeString.substr(2));
47      
48        // Valid time has hours 0-23 and minutes 0-59
49        return hours >= 0 && hours < 24 && minutes >= 0 && minutes < 60;
50    }
51  
52public:
53    /**
54     * Find the next closest time using only the digits from the given time
55     * @param time - input time in "HH:MM" format
56     * @return the next closest time using same digits
57     */
58    string nextClosestTime(string time) {
59        // Convert current time to minutes for easier comparison
60        currentTimeInMinutes = stoi(time.substr(0, 2)) * 60 
61                              + stoi(time.substr(3));
62      
63        // Initialize minimum difference to find closest time
64        minTimeDifference = INT_MAX;
65      
66        // Collect all unique digits from the input time
67        availableDigits.clear();
68        char smallestDigit = '9';  // Initialize to largest possible digit
69        for (char c : time) {
70            if (c != ':') {
71                availableDigits.insert(c);
72                if (c < smallestDigit) {
73                    smallestDigit = c;
74                }
75            }
76        }
77      
78        // Initialize result as empty string
79        result = "";
80      
81        // Try all possible combinations of 4 digits
82        generateTimeCombinations("");
83      
84        // If no valid next time found, return earliest possible time using smallest digit
85        if (result.empty()) {
86            result = string(1, smallestDigit) + smallestDigit + ":" + smallestDigit + smallestDigit;
87        }
88      
89        return result;
90    }
91};
92
1let currentTimeInMinutes: number;
2let minTimeDifference: number;
3let result: string | null;
4let availableDigits: Set<string>;
5
6/**
7 * Find the next closest time using only the digits from the given time
8 * @param time - input time in "HH:MM" format
9 * @return the next closest time using same digits
10 */
11function nextClosestTime(time: string): string {
12    // Convert current time to minutes for easier comparison
13    currentTimeInMinutes = parseInt(time.substring(0, 2)) * 60 + parseInt(time.substring(3));
14  
15    // Initialize minimum difference to find closest time
16    minTimeDifference = Number.MAX_SAFE_INTEGER;
17  
18    // Collect all unique digits from the input time
19    availableDigits = new Set<string>();
20    let smallestDigit: string = '9'; // Initialize with largest possible digit
21  
22    for (const char of time) {
23        if (char !== ':') {
24            availableDigits.add(char);
25            if (char < smallestDigit) {
26                smallestDigit = char;
27            }
28        }
29    }
30  
31    // Initialize result as null
32    result = null;
33  
34    // Try all possible combinations of 4 digits
35    generateTimeCombinations("");
36  
37    // If no valid next time found, return earliest possible time using smallest digit
38    if (result === null) {
39        result = smallestDigit + smallestDigit + ":" + smallestDigit + smallestDigit;
40    }
41  
42    return result;
43}
44
45/**
46 * Recursively generate all possible 4-digit combinations and find the closest valid time
47 * @param currentCombination - current partial combination being built
48 */
49function generateTimeCombinations(currentCombination: string): void {
50    // Base case: when we have 4 digits
51    if (currentCombination.length === 4) {
52        // Check if this forms a valid time
53        if (!isValidTime(currentCombination)) {
54            return;
55        }
56      
57        // Convert to minutes and check if it's the next closest time
58        const candidateTimeInMinutes: number = parseInt(currentCombination.substring(0, 2)) * 60 
59                                              + parseInt(currentCombination.substring(2));
60      
61        // Update result if this time is later than current and closer than previous candidates
62        if (candidateTimeInMinutes > currentTimeInMinutes 
63            && candidateTimeInMinutes - currentTimeInMinutes < minTimeDifference) {
64            minTimeDifference = candidateTimeInMinutes - currentTimeInMinutes;
65            result = currentCombination.substring(0, 2) + ":" + currentCombination.substring(2);
66        }
67        return;
68    }
69  
70    // Try adding each available digit to current combination
71    for (const digit of availableDigits) {
72        generateTimeCombinations(currentCombination + digit);
73    }
74}
75
76/**
77 * Check if a 4-digit string represents a valid time
78 * @param timeString - 4-digit string representing HHMM
79 * @return true if valid time, false otherwise
80 */
81function isValidTime(timeString: string): boolean {
82    const hours: number = parseInt(timeString.substring(0, 2));
83    const minutes: number = parseInt(timeString.substring(2));
84  
85    // Valid time has hours 0-23 and minutes 0-59
86    return hours >= 0 && hours < 24 && minutes >= 0 && minutes < 60;
87}
88

Time and Space Complexity

Time Complexity: O(1)

The algorithm uses DFS to generate all possible 4-digit combinations from the available digits. Since the set s contains at most 4 unique digits (extracted from the time string), and we're building strings of length 4, the total number of recursive calls is 4^4 = 256. Each recursive call performs constant time operations:

  • String concatenation: O(1) for small fixed-size strings
  • Validity check: O(1)
  • Time comparison and update: O(1)

Therefore, the overall time complexity is O(4^4) = O(256) = O(1).

Space Complexity: O(1)

The space usage includes:

  • Set s: stores at most 4 unique digits, so O(1)
  • Recursion stack depth: maximum depth is 4 (building a 4-digit string), so O(1)
  • Variables (ans, d, t, temporary strings): all use constant space O(1)

The total space complexity is O(1).

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

Common Pitfalls

1. Incorrect Wraparound Logic - Not Finding the True Minimum Time

The Pitfall: When no valid time exists after the current time on the same day, the code attempts to find the earliest possible time for the next day by using the smallest digit in all positions. However, this approach is incorrect because it doesn't actually find the earliest valid time that can be formed with the available digits.

For example:

  • Input: "23:59" with digits {2, 3, 5, 9}
  • Current code returns: "22:22" (using smallest digit 2 everywhere)
  • But the actual earliest valid time would be: "22:22" (which happens to be correct in this case)
  • However, for input "22:58" with digits {2, 5, 8}:
    • Current code would try: "22:22" (using smallest digit 2)
    • But the actual earliest time should be: "22:25" (since we need to use available digits)

The Solution: Instead of just using the smallest digit, we need to find the actual minimum valid time from all possible combinations:

def find_earliest_time(available_digits):
    """Find the earliest valid time using available digits."""
    min_time_minutes = float('inf')
    earliest_time = None
  
    def generate_combinations(current):
        nonlocal min_time_minutes, earliest_time
        if len(current) == 4:
            if is_valid_time(current):
                minutes = int(current[:2]) * 60 + int(current[2:])
                if minutes < min_time_minutes:
                    min_time_minutes = minutes
                    earliest_time = current[:2] + ':' + current[2:]
            return
      
        for digit in available_digits:
            generate_combinations(current + digit)
  
    generate_combinations('')
    return earliest_time

# In the main function, replace the wraparound logic:
if closest_time is None:
    closest_time = find_earliest_time(available_digits)

2. Edge Case: All Same Digits

The Pitfall: When all digits in the input are the same (e.g., "22:22"), the algorithm might not handle this correctly because every possible combination yields the same time.

The Solution: Add explicit handling for this case:

# Check if all digits are the same
if len(available_digits) == 1:
    return time  # No other time possible, return the same time

3. Performance Issue with Redundant Computations

The Pitfall: The DFS explores all 4^n combinations where n is the number of unique digits, but many of these combinations might be invalid times. We're wasting computation checking invalid combinations like "99:99".

The Solution: Implement early pruning to avoid generating obviously invalid combinations:

def dfs(current_combination):
    # Early pruning: check partial validity
    if len(current_combination) >= 1:
        # First digit of hour must be 0, 1, or 2
        if current_combination[0] not in '012':
            return
  
    if len(current_combination) >= 2:
        # If first digit is 2, second must be 0-3
        if current_combination[0] == '2' and current_combination[1] not in '0123':
            return
  
    if len(current_combination) >= 3:
        # First digit of minutes must be 0-5
        if current_combination[2] not in '012345':
            return
  
    # Continue with the rest of the DFS logic...

This pruning strategy significantly reduces the search space by eliminating invalid branches early in the recursion tree.

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

Which of the following shows the order of node visit in a Breadth-first Search?


Recommended Readings

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

Load More