Редакция для Сколько раз уже встречалось
Submitting an official solution before solving the problem yourself is a bannable offence.
1. Идея
Нужно обработать обращения слева направо и для каждого идентификатора быстро знать, сколько раз он уже встречался.
Самая естественная структура для этого — словарь:
- ключ — идентификатор пользователя
a_i; - значение — сколько раз он уже встретился.
Когда читаем очередное число x:
- увеличиваем
cnt[x]на 1; - это новое значение и есть ответ для текущей позиции.
2. Наблюдения
- Ответ для позиции
iзависит только от элементов на префиксе, то есть отa_1, a_2, ..., a_i. - Не нужно пересчитывать количество вхождений заново для каждого элемента: это было бы слишком медленно.
- Если хранить текущие количества в словаре, то каждое обращение можно обработать за константное время в среднем.
Пример:
Пусть массив такой: 7 3 7 7 3
Тогда идём по нему:
7→ встречался 1 раз → ответ13→ встречался 1 раз → ответ17→ встречался 2 раза → ответ27→ встречался 3 раза → ответ33→ встречался 2 раза → ответ2
Итог: 1 1 2 3 2
3. Алгоритм
- Считать
n. - Создать словарь
cntдля подсчёта количества появлений каждого идентификатора. - Создать массив ответов
ans. - Для каждого из
nчисел:- считать
x; - увеличить
cnt[x]на 1; - добавить
cnt[x]вans.
- считать
- Вывести элементы массива
ansчерез пробел.
4. Почему это работает
Докажем, что алгоритм выводит правильный ответ для каждой позиции.
Рассмотрим момент обработки некоторого элемента a_i = x.
До этого шага в словаре cnt[x] хранится количество появлений числа x среди уже обработанных элементов:
a_1, a_2, ..., a_(i-1).
После выполнения cnt[x]++ значение cnt[x] становится равным количеству появлений x среди элементов:
a_1, a_2, ..., a_i.
А по условию именно это число и нужно вывести для позиции i: сколько раз идентификатор a_i встретился от начала до текущего места включительно.
Значит, число, которое алгоритм записывает в ans[i], верно.
Так происходит для каждой позиции, следовательно, весь массив ответов строится правильно.
5. Сложность
- Время:
O(n)в среднем, потому что для каждого элемента выполняется одно обращение к словарю. - Память:
O(k), гдеk— количество различных идентификаторов. В худшем случаеO(n).
6. Код на C++17
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
int main() {
int n;
cin >> n;
unordered_map<long long, int> cnt;
vector<int> ans(n);
for (int i = 0; i < n; i++) {
long long x;
cin >> x;
cnt[x]++;
ans[i] = cnt[x];
}
for (int i = 0; i < n; i++) {
if (i > 0) cout << ' ';
cout << ans[i];
}
cout << '\n';
return 0;
}
7. Код на Python 3
n = int(input())
a = list(map(int, input().split()))
cnt = {}
ans = []
for x in a:
cnt[x] = cnt.get(x, 0) + 1
ans.append(cnt[x])
print(*ans)
Комментарии