Как найти повторяющиеся элементы в разных коллекциях за линейное время?

Подскажите плиз с алгоритмом решения задачи, я представляю, что нужно сделать HashMap, ключ будет user, значение список имейлов(я могу их добавлять в hashset например), как потом находить в разных коллекциях повторяющиеся элементы за линейное время?

Имеется n пользователей, каждому из них соответствует список email-ов
(всего у всех пользователей m email-ов).
Например:
user1 ->xxx@ya.ru,foo@gmail.com,lol@mail.ru (xxx@ya.ru,foo@gmail.com,lol@mail.ru)
user2 ->foo@gmail.com,ups@pisem.net (foo@gmail.com,ups@pisem.net)
user3 ->xyz@pisem.net,vasya@pupkin.com (xyz@pisem.net,vasya@pupkin.com)
user4 ->ups@pisem.net,aaa@bbb.ru (ups@pisem.net,aaa@bbb.ru)
user5 ->xyz@pisem.net
Считается, что если у двух пользователей есть общий email, значит это
один и тот же пользователь. Требуется построить
и реализовать алгоритм, выполняющий слияние пользователей. На выходе
должен быть список пользователей с их email-ами (такой же как на
входе).
В качестве имени объединенного пользователя можно брать любое из
исходных имен. Список email-ов пользователя должен содержать только
уникальные email-ы.
Параметры n и m произвольные, длина конкретного списка email-ов никак
не ограничена.
Требуется, чтобы асимптотическое время работы полученного решения было
линейным, или близким к линейному.
  • Вопрос задан
  • 309 просмотров
Пригласить эксперта
Ответы на вопрос 2
wataru
@wataru Куратор тега Алгоритмы
Разработчик на С++, экс-олимпиадник.
Это задача на поиск компонент связности в графе. У вас двудольный граф, но это не важно. Вершины - емейлы и пользователи, ребра - соответствие пользователя емейлу. Решается обходом в глубину или обходом в ширину. Оба решения - линейные от количества ребер (в вашем случае - общее количество емейлов).

Перенумеруйте все емейлы и всех пользователей.
Код будет проще, если емейлы и пользователи хранятся в одном и том же пространстве номеров.
Это реализуется с одним hashMap, который будет давать номер по строке, и одним массивом строк, который будет хранить изначальную строку по номеру. Вам еще понадобится булевый массив, чтобы хранить, является ли данная вершина пользователем или мейлом. При вводе получаете какую-то строку, и вызываете от нее функцию GetID(s, is_user), которая проверяет, есть ли данная строка в мапе. Если есть, возвращает номер. Если нет - дописывает строку в массив строк, записывает ее индекс в мап и возвращает его.

При вводе - постройте граф.
Храните ребра в списке смежности - массив массивов или списков, где для каждого номера-вершины вы построите список всех с ней связанных.
При вводе у вас есть номер вершины-пользователя и вы читаете емейлы и переводите их номера. При этом добавляйте номер-емейл в список для пользователя и наоборот.

Заведите массив пометок "обойденности" для всех вершин. Он будет int. 0 - непосещенные вершины, иначе номер компоненты связности.
Запустите на этом графе DFS/BFS от каждой пока не обойденной вершины в цикле и помечайте все достижимые вершины новым числом (можно передавать вторым параметром в DFS). Можно сразу же во время обхода заполнять структуру ответ - один номер для пользователя и список для емейлов. Или можно после цикла с DFS завести массив списков для ответа, пройтись по массиву и распихать номера вершин по спискам. Используйте булевый массив, чтобы понять какая вершина пользователь, а какая - мейл. Из пользователей возьмите только одного предстваителя, а все емейлы запихайте в список в ответ. Потом выводите, преобразуя номера в строки с помощью имеющегося массива.
Ответ написан
tumbler
@tumbler
бекенд-разработчик на python
По-моему построить обратный индекс "email - список пользователей" как раз будет занимать линейное время (если вставка в хешмап линейна), по дороге можно отмечать те мейлы, в которых в списке более одного элемента оказалось.
Ответ написан
Ваш ответ на вопрос

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

Похожие вопросы