{ }codelper
내포(컴프리헨션)묶기P0123

수강 명단 뒤집기

학생마다 듣는 과목이 적힌 줄 n개가 주어진다.
반복문 대신 집합 내포와 사전 내포(comprehension)로 다음을 만들어 출력하라. 첫 줄에 n이, 다음 n줄에 "이름 과목 과목 ..."이 주어진다.
과목이 하나도 없는 줄도 있다. 같은 학생이 여러 줄에 나올 수 있고, 그 학생의 과목은 그 줄들을 모두 합친 것이다.
한 줄 안에 같은 과목이 거듭 나와도 한 번으로 센다. 1) 서로 다른 학생의 수 S를 출력하고, 이어서 S줄에 "이름 k 과목1 ...
과목k"를 이름 순으로 출력한다.
k는 그 학생이 듣는 서로 다른 과목의 수이고, 과목은 정렬해서 적는다.
과목이 없으면 "이름 0"이다. 2) 서로 다른 과목의 수 C를 출력하고, 이어서 C줄에 "과목 m 이름1 ...
이름m"을 과목 순으로 출력한다.
m은 그 과목을 듣는 학생 수이고, 이름은 정렬해서 적는다. 3) 학생이 한 명뿐인 과목을 정렬해서 한 줄에 빈칸으로 띄워 출력한다.
그런 과목이 없으면 -를 출력한다. 정렬은 모두 문자열의 코드 순서(Python 문자열 비교)로 한다.

입력
1 ≤ n ≤ 1000.
이름은 영어 소문자 1 ~ 8글자.
과목은 영어 대문자와 숫자 2 ~ 6글자이고, 한 줄에 0 ~ 10개, 서로 다른 과목은 200개 이하다.
출력
1) S + 1줄, 2) C + 1줄, 3) 한 줄
예시 1 · stdin
4
kim ART MATH
lee MATH
kim BIO
kim
stdout
2
kim 3 ART BIO MATH
lee 1 MATH
3
ART 1 kim
BIO 1 kim
MATH 2 kim lee
ART BIO
예시 2 · stdin
3
mia CS CS
joy CS ART ART
zoe
stdout
3
joy 2 ART CS
mia 1 CS
zoe 0
2
ART 1 joy
CS 2 joy mia
ART
main.py
입력(stdin)

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

$ 준비됐어요

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

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

이 문제는 목록·집합·사전 내포가 Python 문법이라 Python만 받아요.
다른 언어는 같은 일을 반복문이나 map·filter(JS), 스트림의 groupingBy(Java), LINQ의 GroupBy·ToDictionary(C#)로 해요.
크레이콘이라면 사전()과 각각 반복문, 있나로 만들어요.
사전 내포 {열쇠: 값 for ...}는 집합 내포와 같은 중괄호에 :만 더한 꼴이고, 값 자리에 집합 내포를 넣으면 '학생 → 과목 집합'이 한 줄이 돼요.
첫 낱말과 나머지는 for name, *courses in ...처럼 별표로 풀어 나눠요.
함정 셋: ① {name: set(courses) for name, courses in rows}처럼 줄마다 열쇠를 만들면, 같은 열쇠가 다시 나올 때 오류 없이 마지막 값이 앞의 것을 덮어써요.
서로 다른 학생을 먼저 집합으로 모으고, 값 쪽 내포에서 그 학생의 줄을 모두 합쳐요.
② 값을 목록 내포 [...]로 만들면 한 줄에 거듭 나온 과목이 두 번 담겨 k가 늘어나요.
중복을 없애는 건 집합 내포 {...}예요.
③ dict.fromkeys(과목들, [])는 빈 목록을 하나만 만들어 모든 열쇠에 같은 목록을 넣어요.
그래서 한 과목에 넣은 학생이 모든 과목에 보여요.
열쇠마다 새 값이 필요하면 {c: [] for c in ...}처럼 내포를 써요.
집합에는 차례가 없고 문자열 해시는 실행할 때마다 달라서, 집합을 돌며 만든 사전도 실행마다 차례가 바뀌어요.
출력하기 전에 sorted로 정렬해요.

Pythonmain.py · 20 줄
n = int(input())# 줄마다 (이름, [과목...]) — 별표 풀어 넣기로 첫 낱말과 나머지를 나눈다rows = [(name, courses) for name, *courses in (input().split() for _ in range(n))] # 학생 → 과목 집합. 같은 학생이 여러 줄에 나오면 모두 합친다students = {name for name, _ in rows}taken = {s: {c for name, courses in rows if name == s for c in courses} for s in students} # 과목 → 학생 목록(뒤집기)all_courses = {c for courses in taken.values() for c in courses}roster = {c: sorted(s for s, courses in taken.items() if c in courses) for c in all_courses} print(len(taken))for s in sorted(taken):    print(s, len(taken[s]), *sorted(taken[s]))print(len(roster))for c in sorted(roster):    print(c, len(roster[c]), *roster[c])lonely = sorted(c for c, names in roster.items() if len(names) == 1)print(" ".join(lonely) if lonely else "-")