Ссылка на задачу – 2364. Count Number of Bad Pairs.
📌 Описание задачи
Дан массив nums
, где пара индексов (i, j)
называется плохой, если выполняется:
Требуется найти общее количество таких плохих пар.
💡 Идея
- Запишем условие хорошей пары:
j−i = nums[j]−nums[i]
- Переставляя слагаемые, получаем:
nums[j]−j = nums[i]−i
То есть если два индекса имеют одинаковое значение позиционной разности (nums[k] - k
), – они образуют хорошую пару!
🛠️ Детали метода
- Создаём массив позиционных разностей:
pos_diff[i]=nums[i]−i
- Сортируем массив
pos_diff
, группируя одинаковые значения.
- Используем метод
chunk_by
для подсчёта частот одинаковых значений.
- Для каждой такой частоты
count
, вычисляем количество хороших пар:
count×(count−1)/2
- Всего существует
n × (n - 1) / 2
пар, из них вычитаем хорошие пары и получаем ответ.
⏳ Асимптотика
- Время:
O(n·log n)
, так как сортировка доминирует над остальными операциями.
- Память:
O(n)
, так как храним pos_diff
.
🔥 Хотя сортировка для подсчёта частот медленнее хеш-таблиц в асимптотике, на практике она быстрее из-за низкой константы!
📝 Исходный код
impl Solution {
pub fn count_bad_pairs(nums: Vec<i32>) -> i64 {
let n = nums.len() as i64;
// Compute adjusted values (nums[i] - i) and sort
let mut pos_diff: Vec<_> = nums.into_iter()
.enumerate()
.map(|(idx, num)| num - idx as i32)
.collect();
pos_diff.sort_unstable();
// Count good pairs using `chunk_by`
let good_pairs: i64 = pos_diff
.chunk_by(|a, b| a == b)
.map(|chunk| (chunk.len() as i64 * (chunk.len() as i64 - 1)) / 2)
.sum();
// Total pairs - good pairs = bad pairs
let total_pairs = n * (n - 1) / 2;
total_pairs - good_pairs
}
}
Tags: #rust #algorithms #math
ответить
Ссылка на задачу – 3174. Clear Digits.
📌 Описание задачи
Дана строка s
, содержащая буквы и цифры. Из неё возможно удалять цифры только путем выполнения следующей операции:
- Удалить первую найденную цифру и ближайший нецифровой символ слева.
Вернуть итоговую строку после итеративного выполнения указанной операции до исчезновения всех цифр.
💡 Идея
Достаточно легко придумать алгоритм, формирующий ответ справа-налево (просто запоминаем, сколько символов нужно скипнуть после встреченных цифр).
Мы же реализуем решение с прямым (слева-направо) обходом строки без необходимости переворачивания результата.
- Используем буфер (
buffer
) для хранения промежуточного результата.
- Используем указатель
write_index
, который отслеживает место для записи следующего символа.
- При встрече цифры уменьшаем
write_index
(удаляем ближайший нецифровой символ).
⚙ Подробности подхода
- Инициализируем буфер размером
s.len()
(заполняем заглушками '\0'
).
Читать дальше →
ответить
Ссылка на задачу – 1910. Remove All Occurrences of a Substring.
📌 Описание задачи
Даны две строки s
и part
. Нужно избавиться от всех вхождений part
из s
, выполняя следующую операцию:
- Найти самое левое вхождение
part
в s
и удалить его
- Повторять, пока
part
больше не встречается в s
.
🐢 Анализ наивного решения
Достаточно несложно быстро набросать следующее решение:
impl Solution {
pub fn remove_occurrences(mut s: String, part: String) -> String {
let P = part.len();
while let Some(idx) = s.find(&part) {
s.replace_range(idx..idx+P, "");
}
s
}
}
- Временная сложность такого решения в худшем случае достигает
O(N²/P)
Читать дальше →
1 ответ
Ссылка на задачу – 2342. Max Sum of a Pair With Equal Sum of Digits
📌 Условие задачи
Дан массив положительных чисел nums
. Необходимо найти максимальную сумму двух различных элементов, у которых одинаковая сумма цифр. Если таких пар нет — вернуть -1
.
💡 Идея решения
Вместо хранения всех чисел с одинаковой суммой цифр, достаточно хранить только максимальное из них.
Таким образом, при встрече нового числа с такой же суммой цифр можно сразу проверять, образует ли оно лучшую пару.
🔍 Подход к решению
- Используем массив
max_per_dsum
, в котором храним наибольшее число для каждой суммы цифр.
- Максимальная сумма цифр для i32 — 82, но мы ограничиваем массив сотней для простоты.
- Обрабатываем
nums
за один проход, применяя функциональный стиль с filter_map
:
- вычисляем сумму цифр
d_sum
для числа n
;
- если уже есть число с таким
d_sum
, определяем текущую сумму пары и сохраняем обновлённое максимальное число;
- если это первое число с данным
d_sum
, просто сохраняем его в max_per_dsum
;
filter_map
отбрасывает неопределённые суммы пар (None
), оставляя только валидные.
Читать дальше →
ответить
Ссылка на задачу – 3066. Minimum Operations to Exceed Threshold Value II.
📝 Описание задачи
Дан массив nums
и число k
. Мы можем выполнять следующую операцию, пока в массиве есть хотя бы два элемента:
- Взять два наименьших числа
x
и y
.
- Удалить их из массива.
- Добавить новое число
2·min(x,y) + max(x,y)
.
Необходимо найти количество операций, чтобы все числа в массиве стали ≥ k
.
💡 Идея
Мы объединяем два наименьших числа, поэтому важно быстро находить и извлекать их.
Для этого можно использовать очередь с приоритетами (BinaryHeap
), но в нашем случае можно действовать эффективнее.
🔹 Заведём две очереди (VecDeque
):
orig_values
— содержит исходные числа < k
, отсортированные по возрастанию.
new_values
— хранит новые числа, появляющиеся в процессе операций, в гарантированно возрастающем порядке.
Благодаря двум упорядоченным очередям, минимальные элементы можно извлекать за O(1)
, а вся процедура будет работать эффективнее, чем с BinaryHeap
.
Читать дальше →
ответить
Ссылка на задачу – 1352. Product of the Last K Numbers.
📌 Описание задачи
Необходимо создать структуру ProductOfNumbers
, которая:
- Позволяет добавлять числа в поток (
add(num)
).
- Вычисляет произведение последних k чисел (
get_product(k)
).
Гарантируется, что ответы не приведут к переполнению 32-битного целого.
💡 Идея
Будем использовать префиксные произведения.
Это позволит получать результат за O(1)
, выполняя деление последних значений.
Но так как деление на 0
запрещено, придётся аккуратно отслеживать этот случай и сбрасывать произведения при появлении нуля.
⚙️ Подход
- Используем
Vec<i64>
для хранения префиксных произведений.
- Добавление числа:
- Если
num == 0
, очищаем хранилище, так как любое произведение после нуля будет равно нулю.
- Иначе умножаем предыдущее произведение на текущее число и сохраняем результат.
- Вычисление
get_product(k)
:
- Если
k
больше количества сохранённых чисел, возвращаем 0
.
- Иначе делим последнее префиксное произведение на значение
k
шагов назад.
Читать дальше →
ответить