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

답안지 채점표 만들기

시험의 정답 문자열 K가 주어진다.
문항은 1번부터 L번까지이고(L은 K의 길이), K의 i번째 글자가 i번 문항의 정답이다. 학생 n명의 답안이 주어진다.
답안은 A ~ E와 .(건너뜀)으로 된 문자열이고, 길이는 1 이상 L 이하다. 답안이 L보다 짧으면 모자란 뒤쪽 문항은 건너뛴 것으로 친다. 맞으면 3점, 틀리면 -1점, 건너뛰면 0점이다. 반복문 대신 목록 내포·생성기 식과 zip 계열·enumerate·sorted·itertools.groupby 같은 내장 도구로 다음을 만들어 출력하라. 1) 학생마다 입력 순서대로 "이름 점수 맞은수 틀린수 건너뛴수"를 한 줄씩 출력한다. 2) 점수가 같은 학생끼리 묶어, 점수가 높은 묶음부터 "점수 이름1 이름2 ..."를 한 줄씩 출력한다.
한 묶음 안의 이름은 입력 순서대로 적는다. 3) 틀린 학생이 가장 많은 문항의 번호를 작은 번호부터 한 줄에 빈칸으로 띄워 출력한다.
아무도 틀린 문항이 없으면 -를 출력한다.

입력
첫 줄에 K(영어 대문자 A ~ E, 1 ≤ L ≤ 100).
둘째 줄에 n(1 ≤ n ≤ 1000).
다음 n줄에 "이름 답안".
이름은 영어 소문자와 숫자 1 ~ 10글자이고 서로 다르다.
출력
1) n줄, 2) 서로 다른 점수의 수만큼의 줄, 3) 한 줄
예시 1 · stdin
ABCDE
4
mina ABCDD
joon AB
sora .BCEA
ted EBC
stdout
mina 11 4 1 0
joon 6 2 0 3
sora 4 2 2 1
ted 5 2 1 2
11 mina
6 joon
5 ted
4 sora
5
예시 2 · stdin
CAB
3
amy CAB
bo C.B
cy CAB
stdout
amy 9 3 0 0
bo 6 2 0 1
cy 9 3 0 0
9 amy cy
6 bo
-
main.py
입력(stdin)

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

$ 준비됐어요

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

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

이 문제는 Python만 받아요.
목록 내포와 생성기 식은 Python 문법이고, 다른 언어는 같은 일을 반복문이나 map·filter(JS), 스트림의 groupingBy(Java), LINQ의 Zip·GroupBy(C#)로 해요.
크레이콘이라면 반복과 사전()으로 세요.
문항마다 'O·X·.' 하나로 바꾸는 내포 ["." if a == "." else "O" if a == k else "X" for a, k in ...]에서, 앞에 오는 x if 조건 else y는 값을 고르는 식이고 뒤에 붙는 if는 거르는 조건이라 자리가 달라요.
함정 다섯: ① zip(답안, 정답)은 짧은 쪽에서 멈춰서, 짧은 답안의 모자란 문항이 아예 사라져요(건너뛴 수가 줄어요).
itertools.zip_longest(답안, 정답, fillvalue=".")로 끝까지 짝지어요(3.10부터는 길이가 달라야 할 때 zip(..., strict=True)가 오류를 내 줘요).
② itertools.groupby는 붙어 있는 같은 값만 묶어요.
점수로 먼저 정렬하지 않으면 같은 점수가 여러 묶음으로 갈라져요.
sorted는 안정 정렬이라 reverse=True로 내림차순으로 놓아도 같은 점수의 입력 순서가 남아요.
③ enumerate는 0부터 세요.
문항 번호는 enumerate(..., start=1)로 1부터 세요.
④ 조건 식을 '맞으면 O, 아니면 X'로만 쓰면 건너뛴 .도 틀린 것이 돼요.
⑤ 아무도 틀리지 않으면 가장 많이 틀린 수가 0이라, 그대로 고르면 모든 문항이 뽑혀요.
0이면 빈 목록으로 두고 " ".join(...) or "-"처럼 빈 문자열일 때 -를 내요.
True는 1로 더해지니 sum(m[i] == "X" for ...)처럼 생성기 식으로 바로 셀 수 있어요.

Pythonmain.py · 30 줄
from itertools import groupby, zip_longest key = input().strip()n = int(input())rows = [input().split() for _ in range(n)] # 문항마다 O(맞음)·X(틀림)·.(건너뜀). 짧은 답안은 zip_longest가 "."로 채워 끝까지 짝짓는다sheets = [    (name, ["." if a == "." else "O" if a == k else "X" for a, k in zip_longest(ans, key, fillvalue=".")])    for name, ans in rows] # 1) 학생마다: 이름 점수 맞은수 틀린수 건너뛴수stats = [(name, 3 * m.count("O") - m.count("X"), m.count("O"), m.count("X"), m.count(".")) for name, m in sheets]print("\n".join(" ".join(map(str, s)) for s in stats))  def score(s):    return s[1]  # 2) 같은 점수끼리: groupby는 붙어 있는 것만 묶으므로 먼저 점수로 정렬한다(정렬은 안정적이라 입력 순서가 남는다)ranked = sorted(stats, key=score, reverse=True)print("\n".join(f"{pts} " + " ".join(s[0] for s in grp) for pts, grp in groupby(ranked, key=score))) # 3) 가장 많이 틀린 문항(번호는 1부터)wrong = [sum(m[i] == "X" for _, m in sheets) for i in range(len(key))]top = max(wrong)worst = [q for q, w in enumerate(wrong, start=1) if w == top] if top > 0 else []print(" ".join(map(str, worst)) or "-")