{ }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()・標準入力に渡されます(実行中に入力を求めません)。

$ 準備完了

コードを入力して解くにはパソコンで開いてください。モバイルでは問題と解答を見られます。

ほかの言語では?言語別の解答を見る · 1 言語の解答

この問題は Python だけを受け付けます。
リスト内包表記とジェネレータ式は Python の文法で、ほかの言語は同じことをループや map・filter(JS)、ストリームの groupingBy(Java)、LINQ の Zip・GroupBy(C#)で行います。
Craykon なら 繰り返す と 辞書() で数えます。
問題ごとに 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 "-")