첫 줄에 낱말 수 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
오류 목록00F8 ↓ · ⇧F8 ↑
입력(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;}