Название задания
Описание
Строки в полученом массиве strs
необходимо сгруппировать по анаграммам. Порядок строк и групп в выходном массиве не имеет значения.
Пример 1:
Входные данные: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
Выходные данные: [["bat"], ["nat", "tan"], ["ate", "eat", "tea"]]
Объяснение: каждая строка относиться к той группе в которой все строки являются анограммами
Пример 2:
Входные данные: strs = [""]
Выходные данные: [[""]]
Пример 3:
Входные данные: strs = ["a"]
Выходные данные: [["a"]]
Идея решения
Каждая строка может относиться только к одной группе анаграмм. Две строки будут являться анограммами если имеют одинаковый набор символов, количество вхождений каждого символа должно быть равно у каждой строки. В итоге нам нужно сравнить все строки в отсортированном виде и сгруппировать их по равенству.
Алгоритм решения
Для решения этой задачи будем использовать хэш-таблицу. Ключами будет множество отсортированных строк. Значением для каждого ключа будут все входящие в полученный массив вариации анаграммы.
Алгоритм на Rust аналогично решению на Python, но в связи с особенностями работы с памятью требует дополнительных действий.
Таким образом наш алгоритм выглядит так:
1. создаём хэш-структуру storage
для хренения пар анаграмм:вариациий
2. итеруемся по полученному массиву строк и сохраняем отсортированную строку
3. добавляем текущую строку в массив хранящийся в storage
по хэшу отсортированной строки
4. Возвращаем массив всех значений из storage
Сложность
- По времени: \(O(N * K log K)\) n = len(strs), k = avg([len(i) for i in strs]) Алгоритм состоит из нескольких действий:
- итерация по массиву строк, если длина строки равна
n
то алгоритму потребуется сделатьn
операций - для каждой строки вызывается сортировка, которая имеет сложность \(O(N log N)\),
k
равна средней длине строк - производится вставка в словарь с созданием пустого списка или добавление в список, в средем эта операция происходит за константное время
- в среднем сбор значений из хэш-таблицы выполняется за линейное время и определяется количеством ключей
В данном алгоритме "узким местом" или "бутылочным горлышком" является сортировка. Зная, что входные данные консистентны, и всегда состоят исключительно из символов латинского алфавита в нижем регистре, то можно использовать более подходящий алгоритм сортировки. При использовании сортировки подсчётом можно оптимизировать решение до линейного.
- По памяти: \(O(N * K)\) n = len(strs), k = avg([len(i) for i in strs]) Для реализации алгоритма необходимо хранить все варианты анаграмм. В худшем случае, когда все строки уникальны после сортировки, потребуется хранить каждую строку вместе с её отсортированной версией.
Код решения:
use std::collections::HashMap;
impl Solution {
pub fn group_anagrams(strs: Vec<String>) -> Vec<Vec<String>> {
let mut storage: HashMap<String, Vec<String>> = HashMap::new();
for word in strs {
let mut chars: Vec<char> = word.chars().collect();
chars.sort_unstable();
let word_sorted = chars.into_iter().collect::<String>();
storage.entry(word_sorted).or_insert(Vec::new()).push(word);
}
storage.values().map(|v| v.clone()).collect()
}
}