← Все вопросы

Скользящее окно фиксированной длины с подсчётом различных элементов за O(n)

Задан 9 месяцев назад830 просмотров2 ответа
8

Для каждого окна фиксированной длины k нужно вывести число различных элементов в нём. Если для каждого окна считать заново — O(n*k). Как сделать за O(n), обновляя ответ при сдвиге окна на 1?

2 ответа

13
✓ Принятый ответ — помог автору

Держим частотную таблицу элементов текущего окна и счётчик distinct. При сдвиге окна вправо на 1: добавляем новый правый элемент и убираем ушедший левый, обновляя distinct инкрементально.

Правило обновления:

  • При добавлении x: если его частота была 0 → distinct++, потом cnt[x]++.
  • При удалении x: cnt[x]--, и если стала 0 → distinct--.
vector<int> distinctInWindows(const vector<int>& a, int k) {
    unordered_map<int,int> cnt;
    int distinct = 0;
    vector<int> res;
    for (int i = 0; i < (int)a.size(); i++) {
        if (cnt[a[i]]++ == 0) distinct++;            // вошёл правый
        if (i >= k) {                                // вышел левый
            if (--cnt[a[i - k]] == 0) distinct--;
        }
        if (i >= k - 1) res.push_back(distinct);     // окно сформировано
    }
    return res;
}

Каждый элемент один раз входит в окно и один раз выходит → суммарно O(n) операций (амортизированно O(1) на сдвиг). Если значения большие/разреженные — unordered_map, иначе обычный массив частот по сжатым значениям (быстрее, без хеш-константы). Память O(min(n, k) + alphabet).

Ключ — обновлять distinct в момент перехода частоты через ноль: 0→1 при добавлении даёт +1, 1→0 при удалении даёт −1. Это и есть весь инвариант скользящего окна для distinct.

5

Грабля производительности: unordered_map в C++ имеет большую константу и его легко «положить» антихеш-тестами (специально подобранный вход → коллизии → O(n) на операцию → TLE). Если значения помещаются в массив (после координатного сжатия до диапазона [0, n)), используй vector<int> cnt(n, 0) — это и быстрее, и устойчиво к антихешу. Координатное сжатие: собери уникальные значения, отсортируй, замени каждый элемент его индексом в отсортированном списке — O(n log n) один раз, дальше окно за O(n).

Ваш ответ

Войдите, чтобы ответить на вопрос.