{ }codelper
묶기사전 돌기기준 정해 정렬P0108

애너그램 묶기

첫 줄에 낱말 수 n이, 둘째 줄에 영어 소문자 낱말 n개가 빈칸 하나씩으로 나뉘어 주어진다.
글자를 늘어놓는 차례만 다르고 같은 글자를 같은 개수만큼 쓴 낱말들을 애너그램이라고 한다(eat, tea, ate).
낱말을 애너그램끼리 묶어라.
같은 낱말이 여러 번 나오면 나온 만큼 모두 그 묶음에 넣는다.
묶음마다 한 줄에 "개수: 낱말 낱말 …"을 출력한다.
묶음 안의 낱말은 사전 순(앞 글자부터 비교해 알파벳이 앞선 쪽이 먼저, 한쪽이 다른 쪽의 앞부분이면 짧은 쪽이 먼저)으로 쓴다.
묶음은 낱말이 많은 것부터 출력한다.
낱말 수가 같으면 두 묶음의 첫 낱말(정렬한 묶음에서 맨 앞 낱말)을 사전 순으로 비교해 앞선 묶음을 먼저 출력한다.

입력
1 ≤ n ≤ 5000, 각 낱말은 영어 소문자 1 ~ 10글자
출력
묶음 수만큼의 줄.
줄마다 낱말 수, 쌍점(:), 그리고 낱말마다 빈칸 하나 뒤에 낱말
예시 1 · stdin
6
eat tea tan ate nat bat
stdout
3: ate eat tea
2: nat tan
1: bat
예시 2 · stdin
7
listen silent enlist google banana elgoog tinsel
stdout
4: enlist listen silent tinsel
2: elgoog google
1: banana
main.py
입력(stdin)

실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).

$ 준비됐어요

코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.

다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이

애너그램은 글자를 정렬한 문자열이 같아요(eat, tea → aet).
이것을 사전의 열쇠로 묶으면 돼요.
함정은 열쇠의 타입이에요.
Python sorted(w)는 목록이라 열쇠로 쓰면 TypeError가 나니 "".join(sorted(w))로 문자열을 만들어요.
JS [...w].sort() 배열을 그대로 Map 열쇠로 쓰면 배열은 같은 객체일 때만 같아서 낱말마다 따로 묶여요(.join("")를 붙여요).
Java·C#도 char[]를 열쇠로 쓰면 내용이 아니라 배열 객체로 비교하니 new String(c)·new string(c)로 바꿔요.
C++ std::string은 값이라 정렬한 복사본이 그대로 열쇠가 돼요.
크레이콘은 글자를 목록에 담아 정렬()한 뒤 잇기("")로 열쇠를 만들어요.
글자 코드의 합을 열쇠로 쓰면 ad와 bc(97+100 = 98+99)가 한 묶음이 되는 것도 흔한 실수예요.
C에는 사전이 없어서 (정렬한 글자, 낱말) 차례로 qsort하면 같은 묶음이 붙어 나오고 묶음 안도 사전 순이 돼요.
묶음을 돌 때 Python dict·JS Map·크레이콘 열쇠목록은 처음 넣은 차례, Java HashMap·C# Dictionary는 차례를 약속하지 않고, C++ std::map은 열쇠 차례예요.
어느 쪽이든 출력 차례와는 다르니 "큰 묶음 먼저, 같으면 첫 낱말"로 다시 정렬해야 해요.
크기로만 정렬하면 크기가 같은 묶음이 처음 나온 차례로 남아요.

Pythonmain.py · 13 줄
n = int(input())words = input().split() # 글자를 정렬한 문자열이 같으면 같은 묶음이다(eat, tea → "aet")groups = {}for w in words:    key = "".join(sorted(w))      # sorted(w)는 목록이라 사전 열쇠가 될 수 없다    groups.setdefault(key, []).append(w) result = [sorted(g) for g in groups.values()]result.sort(key=lambda g: (-len(g), g[0]))   # 큰 묶음 먼저, 같으면 첫 낱말 사전 순for g in result:    print(f"{len(g)}: {' '.join(g)}")
JavaScriptmain.js · 14 줄
const [n, ...words] = require("fs").readFileSync(0, "utf8").trim().split(/\s+/); // 글자를 정렬한 문자열이 같으면 같은 묶음이다(eat, tea → "aet")const groups = new Map();for (const w of words) {  const key = [...w].sort().join(""); // 배열 그대로는 열쇠로 못 쓴다(배열은 같은 객체일 때만 같다)  if (!groups.has(key)) groups.set(key, []);  groups.get(key).push(w);} const result = [...groups.values()].map((g) => g.sort());const cmp = (a, b) => (a < b ? -1 : a > b ? 1 : 0);result.sort((a, b) => b.length - a.length || cmp(a[0], b[0])); // 큰 묶음 먼저, 같으면 첫 낱말console.log(result.map((g) => `${g.length}: ${g.join(" ")}`).join("\n"));
TypeScriptmain.ts · 16 줄
import { readFileSync } from "node:fs";const [, ...words]: string[] = readFileSync(0, "utf8").trim().split(/\s+/); // 글자를 정렬한 문자열이 같으면 같은 묶음이다(eat, tea → "aet")const groups = new Map<string, string[]>();for (const w of words) {  const key = [...w].sort().join(""); // 배열 그대로는 열쇠로 못 쓴다(배열은 같은 객체일 때만 같다)  const g = groups.get(key);  if (g) g.push(w);  else groups.set(key, [w]);} const result: string[][] = [...groups.values()].map((g) => g.sort());const cmp = (a: string, b: string): number => (a < b ? -1 : a > b ? 1 : 0);result.sort((a, b) => b.length - a.length || cmp(a[0], b[0])); // 큰 묶음 먼저, 같으면 첫 낱말console.log(result.map((g) => `${g.length}: ${g.join(" ")}`).join("\n"));
JavaMain.java · 30 줄
import java.io.*;import java.util.*; public class Main {    public static void main(String[] args) throws IOException {        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        br.readLine();        String[] words = br.readLine().trim().split(" ");         // 글자를 정렬한 문자열이 같으면 같은 묶음이다(eat, tea → "aet")        Map<String, List<String>> groups = new HashMap<>();        for (String w : words) {            char[] c = w.toCharArray();            Arrays.sort(c);            String key = new String(c); // char[] 그대로는 열쇠가 안 된다(배열은 내용이 아니라 객체로 비교)            groups.computeIfAbsent(key, k -> new ArrayList<>()).add(w);        }         List<List<String>> result = new ArrayList<>();        for (List<String> g : groups.values()) {            Collections.sort(g);            result.add(g);        }        result.sort(Comparator.<List<String>>comparingInt(g -> -g.size()).thenComparing(g -> g.get(0)));         StringBuilder out = new StringBuilder();        for (List<String> g : result) out.append(g.size()).append(": ").append(String.join(" ", g)).append('\n');        System.out.print(out);    }}
C#Main.cs · 34 줄
using System;using System.Collections.Generic;using System.Linq; class Program{    static void Main()    {        Console.ReadLine();        string[] words = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);         // 글자를 정렬한 문자열이 같으면 같은 묶음이다(eat, tea → "aet")        var groups = new Dictionary<string, List<string>>();        foreach (string w in words)        {            char[] c = w.ToCharArray();            Array.Sort(c);            string key = new string(c); // char[] 그대로는 열쇠가 안 된다(배열은 객체로 비교)            if (!groups.TryGetValue(key, out var list))            {                list = new List<string>();                groups[key] = list;            }            list.Add(w);        }         // 문자열은 Ordinal로 비교한다(기본 비교는 문화권 규칙을 따른다)        var result = groups.Values            .Select(g => g.OrderBy(w => w, StringComparer.Ordinal).ToList())            .OrderByDescending(g => g.Count)            .ThenBy(g => g[0], StringComparer.Ordinal);        foreach (var g in result) Console.WriteLine($"{g.Count}: {string.Join(" ", g)}");    }}
C++main.cpp · 35 줄
#include <algorithm>#include <iostream>#include <map>#include <string>#include <vector> int main() {    int n;    std::cin >> n;    // 글자를 정렬한 문자열이 같으면 같은 묶음이다(eat, tea → "aet")    std::map<std::string, std::vector<std::string>> groups;    for (int i = 0; i < n; i++) {        std::string w;        std::cin >> w;        std::string key = w;        std::sort(key.begin(), key.end()); // std::string은 값이라 정렬한 복사본이 곧 열쇠        groups[key].push_back(w);          // []가 없으면 빈 vector를 만들어 준다    }     std::vector<std::vector<std::string>> result;    for (auto& [key, g] : groups) {        std::sort(g.begin(), g.end());        result.push_back(g);    }    std::sort(result.begin(), result.end(), [](const auto& a, const auto& b) {        if (a.size() != b.size()) return a.size() > b.size(); // 큰 묶음 먼저        return a[0] < b[0];                                    // 같으면 첫 낱말 사전 순    });     for (const auto& g : result) {        std::cout << g.size() << ":";        for (const auto& w : g) std::cout << " " << w;        std::cout << "\n";    }}
Cmain.c · 55 줄
#include <stdio.h>#include <stdlib.h>#include <string.h> /* C에는 사전이 없다: (정렬한 글자, 낱말) 차례로 정렬하면 같은 묶음이 붙어 나오고 묶음 안도 사전 순이 된다 */typedef struct {    char key[11];    char word[11];} Item; typedef struct {    int start, size; /* items[start] ~ items[start + size - 1] */} Group; static Item items[5000];static Group groups[5000]; static int cmp_char(const void *a, const void *b) { return *(const char *)a - *(const char *)b; } static int cmp_item(const void *a, const void *b) {    const Item *x = a, *y = b;    int c = strcmp(x->key, y->key);    return c != 0 ? c : strcmp(x->word, y->word);} static int cmp_group(const void *a, const void *b) {    const Group *x = a, *y = b;    if (x->size != y->size) return y->size - x->size;           /* 큰 묶음 먼저 */    return strcmp(items[x->start].word, items[y->start].word); /* 같으면 첫 낱말 */} int main(void) {    int n;    scanf("%d", &n);    for (int i = 0; i < n; i++) {        scanf("%10s", items[i].word);        strcpy(items[i].key, items[i].word);        qsort(items[i].key, strlen(items[i].key), 1, cmp_char);    }    qsort(items, n, sizeof(Item), cmp_item);     int g = 0;    for (int i = 0; i < n; i++) {        if (i == 0 || strcmp(items[i].key, items[i - 1].key) != 0) groups[g++] = (Group){i, 0};        groups[g - 1].size++;    }    qsort(groups, g, sizeof(Group), cmp_group);     for (int i = 0; i < g; i++) {        printf("%d:", groups[i].size);        for (int j = 0; j < groups[i].size; j++) printf(" %s", items[groups[i].start + j].word);        printf("\n");    }    return 0;}
Craykonmain.cray · 52 줄
콘솔.줄읽기();words = 콘솔.줄읽기().공백제거().나누기(); // 글자를 정렬한 문자열이 같으면 같은 묶음이다(eat, tea → "aet")groups = 사전();각각 (w 에서 words){    key = 글자정렬(w);    만약 (!groups.있나(key))    {        groups[key] = [];    }    groups[key].넣기(w);} keys = groups.열쇠목록;각각 (key 에서 keys){    groups[key].정렬();}keys.정렬(큰묶음먼저);각각 (key 에서 keys){    g = groups[key];    콘솔.쓰기(g.개수 + ": " + g.잇기(" "));} // 낱말의 글자를 하나씩 목록에 담아 정렬한 뒤 다시 잇는다글자정렬(w){    c = [];    반복 (i = 0; i < w.길이; i++)    {        c.넣기(w[i]);    }    c.정렬();    돌려주기 c.잇기("");} // 큰 묶음 먼저, 크기가 같으면 첫 낱말이 사전 순으로 앞선 묶음 먼저큰묶음먼저(앞, 뒤){    만약 (groups[앞].개수 != groups[뒤].개수)    {        돌려주기 groups[뒤].개수 - groups[앞].개수;    }    만약 (groups[앞][0] < groups[뒤][0])    {        돌려주기 -1;    }    돌려주기 1;}