Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Move from brute-force thinking to an efficient approach using array strategy.
Given an array arr of 4 digits, find the latest 24-hour time that can be made using each digit exactly once.
24-hour times are formatted as "HH:MM", where HH is between 00 and 23, and MM is between 00 and 59. The earliest 24-hour time is 00:00, and the latest is 23:59.
Return the latest 24-hour time in "HH:MM" format. If no valid time can be made, return an empty string.
Example 1:
Input: arr = [1,2,3,4] Output: "23:41" Explanation: The valid 24-hour times are "12:34", "12:43", "13:24", "13:42", "14:23", "14:32", "21:34", "21:43", "23:14", and "23:41". Of these times, "23:41" is the latest.
Example 2:
Input: arr = [5,5,5,5] Output: "" Explanation: There are no valid 24-hour times as "55:55" is not valid.
Constraints:
arr.length == 40 <= arr[i] <= 9Problem summary: Given an array arr of 4 digits, find the latest 24-hour time that can be made using each digit exactly once. 24-hour times are formatted as "HH:MM", where HH is between 00 and 23, and MM is between 00 and 59. The earliest 24-hour time is 00:00, and the latest is 23:59. Return the latest 24-hour time in "HH:MM" format. If no valid time can be made, return an empty string.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Backtracking
[1,2,3,4]
[5,5,5,5]
number-of-valid-clock-times)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #949: Largest Time for Given Digits
class Solution {
public String largestTimeFromDigits(int[] arr) {
int ans = -1;
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
for (int k = 0; k < 4; ++k) {
if (i != j && j != k && i != k) {
int h = arr[i] * 10 + arr[j];
int m = arr[k] * 10 + arr[6 - i - j - k];
if (h < 24 && m < 60) {
ans = Math.max(ans, h * 60 + m);
}
}
}
}
}
return ans < 0 ? "" : String.format("%02d:%02d", ans / 60, ans % 60);
}
}
// Accepted solution for LeetCode #949: Largest Time for Given Digits
func largestTimeFromDigits(arr []int) string {
ans := -1
for i := 0; i < 4; i++ {
for j := 0; j < 4; j++ {
for k := 0; k < 4; k++ {
if i != j && j != k && i != k {
h := arr[i]*10 + arr[j]
m := arr[k]*10 + arr[6-i-j-k]
if h < 24 && m < 60 {
ans = max(ans, h*60+m)
}
}
}
}
}
if ans < 0 {
return ""
}
return fmt.Sprintf("%02d:%02d", ans/60, ans%60)
}
# Accepted solution for LeetCode #949: Largest Time for Given Digits
class Solution:
def largestTimeFromDigits(self, arr: List[int]) -> str:
cnt = [0] * 10
for v in arr:
cnt[v] += 1
for h in range(23, -1, -1):
for m in range(59, -1, -1):
t = [0] * 10
t[h // 10] += 1
t[h % 10] += 1
t[m // 10] += 1
t[m % 10] += 1
if cnt == t:
return f'{h:02}:{m:02}'
return ''
// Accepted solution for LeetCode #949: Largest Time for Given Digits
struct Solution;
use std::fmt;
#[derive(Debug, PartialEq, Eq, Clone)]
struct Time {
hour: i32,
minute: i32,
}
impl Time {
fn new(hour: i32, minute: i32) -> Self {
Time { hour, minute }
}
fn is_valid(&self) -> bool {
self.hour < 24 && self.minute < 60
}
fn from_digits(a: &[i32]) -> Self {
Self::new(a[0] * 10 + a[1], a[2] * 10 + a[3])
}
fn to_minutes(&self) -> i32 {
self.hour * 60 + self.minute
}
fn to_digits(&self) -> Vec<i32> {
vec![
self.hour / 10,
self.hour % 10,
self.minute / 10,
self.minute % 10,
]
}
}
impl fmt::Display for Time {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let a = self.to_digits();
write!(f, "{}{}:{}{}", a[0], a[1], a[2], a[3])
}
}
use std::cmp::Ordering;
impl PartialOrd for Time {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.to_minutes().cmp(&other.to_minutes()))
}
}
impl Solution {
fn backtrack(a: &mut Vec<i32>, index: usize, max: &mut Option<Time>) {
let n = a.len();
if index == n {
let time = Time::from_digits(a);
if time.is_valid() {
if let Some(max_time) = max {
if time > *max_time {
*max = Some(time)
}
} else {
*max = Some(time)
}
}
} else {
for i in index..n {
a.swap(index, i);
Self::backtrack(a, index + 1, max);
a.swap(index, i);
}
}
}
fn largest_time_from_digits(mut a: Vec<i32>) -> String {
let mut max: Option<Time> = None;
Self::backtrack(&mut a, 0, &mut max);
if let Some(max_time) = max {
max_time.to_string()
} else {
"".to_string()
}
}
}
#[test]
fn test() {
let a = vec![1, 2, 3, 4];
let res = "23:41".to_string();
assert_eq!(Solution::largest_time_from_digits(a), res);
let a = vec![5, 5, 5, 5];
let res = "".to_string();
assert_eq!(Solution::largest_time_from_digits(a), res);
let a = vec![0, 0, 0, 0];
let res = "00:00".to_string();
assert_eq!(Solution::largest_time_from_digits(a), res);
}
// Accepted solution for LeetCode #949: Largest Time for Given Digits
// Auto-generated TypeScript example from java.
function exampleSolution(): void {
}
// Reference (java):
// // Accepted solution for LeetCode #949: Largest Time for Given Digits
// class Solution {
// public String largestTimeFromDigits(int[] arr) {
// int ans = -1;
// for (int i = 0; i < 4; ++i) {
// for (int j = 0; j < 4; ++j) {
// for (int k = 0; k < 4; ++k) {
// if (i != j && j != k && i != k) {
// int h = arr[i] * 10 + arr[j];
// int m = arr[k] * 10 + arr[6 - i - j - k];
// if (h < 24 && m < 60) {
// ans = Math.max(ans, h * 60 + m);
// }
// }
// }
// }
// }
// return ans < 0 ? "" : String.format("%02d:%02d", ans / 60, ans % 60);
// }
// }
Use this to step through a reusable interview workflow for this problem.
Generate every possible combination without any filtering. At each of n positions we choose from up to n options, giving nⁿ total candidates. Each candidate takes O(n) to validate. No pruning means we waste time on clearly invalid partial solutions.
Backtracking explores a decision tree, but prunes branches that violate constraints early. Worst case is still factorial or exponential, but pruning dramatically reduces the constant factor in practice. Space is the recursion depth (usually O(n) for n-level decisions).
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: Mutable state leaks between branches.
Usually fails on: Later branches inherit selections from earlier branches.
Fix: Always revert state changes immediately after recursive call.