Уникальные слова по признаку анаграмм (оставить по одному представителю)

90. Уникальные слова по признаку анаграмм (оставить по одному представителю)

Условие задачи:
📌 Дана коллекция слов. Нужно получить уникальные слова, где уникальность определяется так:

  • слова считаются одинаковыми, если одно является анаграммой другого (без учёта регистра);

  • в результате нужно оставить по одному слову из каждой группы анаграмм (например, первое встретившееся).
    Повторы одного и того же слова (например, "angel" встречается дважды) тоже должны схлопываться в один элемент.

Код:

import java.util.*;
import java.util.stream.Collectors;

class MyCode {
    public static void main (String[] args) {
        List<String> anagrams = List.of("Race", "NIghT", "Angle", "CaRe", "angel", "ThiNG", "agnel", "angel");
        // ожидаемо: по одному слову на каждую "анаграмм-группу"
        // например: ["Race", "NIghT", "Angle"] (если берём первый встретившийся)
        System.out.println(removeAnagrams(anagrams));
    }

    //need to implement
    public static List<String> removeAnagrams(List<String> anagrams){
        return anagrams;
    }
}
Спойлеры к решению
Подсказки
💡 Для каждого слова постройте канонический ключ: lowercase + отсортированные буквы.
💡 Используйте Map<key, originalWord>, чтобы хранить первое слово в группе.
💡 Чтобы сохранить порядок появления групп — берите LinkedHashMap.
💡 В конце верните map.values() как список.
Решение
public static List<String> removeAnagrams(List<String> words) {
    if (words == null || words.isEmpty()) return List.of();

    // key -> representative (первое встретившееся слово этой анаграмм-группы)
    Map<String, String> repByKey = new LinkedHashMap<>();

    for (String w : words) {
        if (w == null) continue; // по желанию: можно либо пропускать, либо падать ошибкой

        String key = canonicalKey(w);
        repByKey.putIfAbsent(key, w);
    }

    return new ArrayList<>(repByKey.values());
}

private static String canonicalKey(String s) {
    char[] chars = s.toLowerCase(Locale.ROOT).toCharArray();
    Arrays.sort(chars);
    return new String(chars);
}

Почему так:

  • LinkedHashMap сохраняет порядок вставки → группы идут в порядке первого появления.

  • putIfAbsent гарантирует, что мы сохраняем первый представитель группы.

  • Канонический ключ одинаковый у всех анаграмм → они схлопываются.