Признак в условии
- «непрерывный участок» массива или строки - подряд, не подпоследовательность
- «самый длинный / самый короткий, при условии …»
- «не более K различных», «ровно K», «сумма не меньше target»
Идея
Окно [left, right]. Расширяем right всегда, чиним left, пока условие нарушено. Ответ записываем там, где окно допустимо. Каждый индекс входит и выходит из окна максимум по разу - отсюда O(n), несмотря на вложенный while.
Шаблон
let left = 0, best = 0;
const count = new Map();
for (let right = 0; right < s.length; right++) {
add(s[right], count);
while (!ok(count)) { // окно испортилось: чиним слева
remove(s[left], count);
left++;
}
best = Math.max(best, right - left + 1);
}
Сложность
Время O(n), память O(размер алфавита) или O(k).
Частые ошибки
- отрицательные числа: сумма перестаёт быть монотонной, окно не работает - нужны префиксные суммы
- нулевые счётчики не удаляются из Map, и
sizeперестаёт отражать число различных - ответ фиксируется вне проверки условия
- left уезжает назад (при работе с «последней позицией символа» двигать только вперёд)
Мнемоника
«Расширяй справа, чини слева, записывай ответ»
Не путать с
- Два указателя - оба двигают два индекса, но у окна между ними непрерывный участок, который что-то накапливает; у двух указателей между ними ничего нет, они просто сходятся
- Префиксные суммы - окно годится, когда числа неотрицательные и сумма монотонна. Появились отрицательные - окно ломается, нужны префиксные суммы с хеш-таблицей
- Хеш-таблица - если участок не обязан быть непрерывным, окна нет - есть просто подсчёт
Задачи на этот паттерн
- самая длинная подстрока без повторов
- максимальная сумма окна длины k
- самая короткая подстрока с k различными
- кратчайший подмассив с суммой ≥ target
- максимум гласных в окне
- самая длинная серия после k замен
- перестановка как подстрока
- самая длинная серия единиц с k заменами