681. Next Closest Time 🔒
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 digits1
,9
,3
, and4
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
to23
, minutes from00
to59
) - 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 digits1
,9
,3
,4
) - Input:
"23:59"
might return"22:22"
(wrapping to next day's earliest time with digits2
,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).
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:
- Extract all unique digits from the input time (ignoring the colon)
- Generate all possible 4-digit combinations using these digits
- For each combination, check if it forms a valid time (hours < 24, minutes < 60)
- 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 EvaluatorExample 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, soO(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 spaceO(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 digit2
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 digit2
) - But the actual earliest time should be:
"22:25"
(since we need to use available digits)
- Current code would try:
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.
Which of the following shows the order of node visit in a Breadth-first Search?
Recommended Readings
Backtracking Template Prereq DFS with States problems dfs_with_states Combinatorial search problems Combinatorial search problems involve finding groupings and assignments of objects that satisfy certain conditions Finding all permutations combinations subsets and solving Sudoku are classic combinatorial problems The time complexity of combinatorial problems often grows rapidly with the size of
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!