Ссылка на задачу – 1718. Construct the Lexicographically Largest Valid Sequence.
📌 Условие задачи
Дано число n
. Необходимо построить последовательность, удовлетворяющую следующим условиям:
- Число
1
встречается ровно один раз.
- Каждое число
i
от 2
до n
встречается ровно дважды.
- Для каждого числа
i > 1
, два его вхождения в последовательность находятся на расстоянии ровно i
.
- Среди всех возможных последовательностей требуется выбрать лексикографически наибольшую.
💡 Идея
Бэктрекинг — хороший способ решения данной задачи.
Чтобы получить лексикографически наибольшую последовательность, мы должны размещать сначала наибольшие числа.
🔄 Подробности метода
- Рекурсивно заполняем последовательность, начиная с первого свободного индекса.
- Используем массив
used
, который отслеживает, какие числа уже размещены.
- Пропускаем занятые позиции.
- Проверяем возможность размещения числа заранее, прежде чем выполнять рекурсию.
- Используем
debug_assert!
в remove_value
, чтобы отловить ошибки при отладке.
⏳ Ассимптотика
- Временная сложность:
O(n!)
– в худшем случае, но за счёт отсечения не все ветви перебираются.
- По памяти:
O(n)
– за счёт хранения массива состояния и глубины рекурсии.
📜 Исходный код
impl Solution {
pub fn construct_distanced_sequence(n: i32) -> Vec<i32> {
let size = (2 * n - 1) as usize;
let mut seq = vec![0; size]; // Sequence of length 2n - 1
let mut used = vec![false; (n + 1) as usize]; // Tracks used numbers
// Start backtracking
if !Self::backtrack(0, &mut used[..], &mut seq[..]) {
panic!("No valid sequence exists"); // Should never occur per problem constraints
}
seq
}
fn backtrack(pos: usize, used: &mut [bool], seq: &mut [i32]) -> bool {
// If we've filled the sequence, return success
if pos >= seq.len() {
return true;
}
// Skip already placed numbers and move forward
if seq[pos] != 0 {
return Self::backtrack(pos + 1, used, seq);
}
let n = used.len() - 1; // Infer `n` from `used` length
// Try placing numbers from largest to smallest (ensuring lexicographical order)
for num in (1..=n).rev() {
if used[num] {
continue; // Skip numbers already in use
}
// Place the number if valid
if Self::place_value(seq, pos, num as i32) {
used[num] = true;
if Self::backtrack(pos + 1, used, seq) {
return true; // If successful, return immediately
}
Self::remove_value(seq, pos); // Undo placement if it leads to a dead-end
used[num] = false;
}
}
false // No valid placement found at this position
}
fn place_value(seq: &mut [i32], pos: usize, num: i32) -> bool {
if seq[pos] != 0 {
return false; // Position already occupied
}
if num == 1 {
seq[pos] = num; // 1 appears only once
return true;
}
let second_pos = pos + num as usize;
if second_pos < seq.len() && seq[second_pos] == 0 {
seq[pos] = num;
seq[second_pos] = num; // Place number at correct distance
return true;
}
false // Placement not possible
}
fn remove_value(seq: &mut [i32], pos: usize) {
let num = seq[pos];
// Ensure we are removing a valid number
debug_assert!(num > 0, "Attempting to remove from an empty position: pos={}, seq={:?}", pos, seq);
// Ensure correct removal of the second occurrence
if num > 1 {
let second_pos = pos + num as usize;
debug_assert!(
second_pos < seq.len() && seq[second_pos] == num,
"Invalid state: expected second occurrence of {} at position {}, seq={:?}",
num, second_pos, seq
);
seq[second_pos] = 0;
}
seq[pos] = 0;
}
}
Tags: #rust #algorithms #backtracking