{ }codelper
辞書で数えるP0220

読み取った順に商品を数える

レジで商品 n 個のコードを一つずつ読み取った。
一行目に n、二行目に読み取った順にコード n 個が空白で区切って与えられる。
コードは英小文字と数字からなる 1〜8 文字の文字列である。
コードは文字列のまま比べる。
だから "007" と "7" は別のコードである。
異なるコードごとに、一行にコードと読み取られた回数を空白一つで区切って出力せよ。
行の順は、そのコードが初めて読み取られた順である。

入力
1 ≤ n ≤ 5,000、コードは英小文字(a〜z)と数字(0〜9)からなる 1〜8 文字
出力
異なるコードごとに一行(コード、回数)、初めて読み取られた順
例 1 · stdin
6
milk 42 egg 7 42 milk
stdout
milk 2
42 2
egg 1
7 1
例 2 · stdin
4
007 7 07 7
stdout
007 1
7 2
07 1
main.py
入力(stdin)

実行すると、これらの行が順に input()・標準入力に渡されます(実行中に入力を求めません)。

$ 準備完了

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

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

数えるのは辞書一つで足ります。
Python counts[c] = counts.get(c, 0) + 1、JS・TS Map の set(c, (get(c) ?? 0) + 1)、Java merge(c, 1, Integer::sum)、C# TryGetValue、C++ counts[c]++(ないキーは 0 で作られます)、Craykon 辞書() と 含む です。
この問題の落とし穴は、回すときの順番です。
Python dict(3.7 から)、JS・TS Map、Java LinkedHashMap、Craykon の辞書の キー一覧 は入れた順を守ります。
Java HashMap と C++ unordered_map は順番がばらばらになり、C++ std::map はキーの辞書順で回ります。
C# Dictionary は削除しなければ入れた順に回るように見えますが、ドキュメントは約束していないので、初めて出たコードを別のリストに書いておくのが安全です。
JS のオブジェクト {} も落とし穴です。"42" のように先頭に 0 のない 0 以上の整数として読めるキーは、入れた順に関係なく小さい数から先頭に来ます("007" は違います)。
また JS・TS でいつもの .split(/\s+/).map(Number) で読むと "007" は 7 になり、文字のコードはすべて NaN になるので、コードは文字列のままにします。
C には辞書がないので、初めて出た順に配列に書き、strcmp で先頭から探します。

Pythonmain.py · 7 行
n = int(input())counts = {}for code in input().split():    counts[code] = counts.get(code, 0) + 1# dict는 처음 넣은 차례를 지킨다(3.7부터 언어 규칙)for code, c in counts.items():    print(code, c)
JavaScriptmain.js · 9 行
const [n, ...codes] = require("fs").readFileSync(0, "utf8").trim().split(/\s+/);// Map은 처음 넣은 차례를 지킨다. 객체 {}는 "42" 같은 숫자 열쇠를 앞으로 당긴다const counts = new Map();for (const code of codes) {  counts.set(code, (counts.get(code) ?? 0) + 1);}const out = [];for (const [code, c] of counts) out.push(`${code} ${c}`);console.log(out.join("\n"));
TypeScriptmain.ts · 9 行
import { readFileSync } from "node:fs";const [n, ...codes]: string[] = readFileSync(0, "utf8").trim().split(/\s+/);const counts = new Map<string, number>();for (const code of codes) {  counts.set(code, (counts.get(code) ?? 0) + 1);}const out: string[] = [];for (const [code, c] of counts) out.push(`${code} ${c}`);console.log(out.join("\n"));
JavaMain.java · 21 行
import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.LinkedHashMap;import java.util.Map; public class Main {    public static void main(String[] args) throws Exception {        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        br.readLine();        // HashMap은 차례가 뒤섞인다. LinkedHashMap이 넣은 차례를 지킨다        Map<String, Integer> counts = new LinkedHashMap<>();        for (String code : br.readLine().trim().split("\\s+")) {            counts.merge(code, 1, Integer::sum);        }        StringBuilder out = new StringBuilder();        for (Map.Entry<String, Integer> e : counts.entrySet()) {            out.append(e.getKey()).append(' ').append(e.getValue()).append('\n');        }        System.out.print(out);    }}
C#Main.cs · 29 行
using System;using System.Collections.Generic;using System.Text; class Program{    static void Main()    {        Console.ReadLine();        // Dictionary의 차례는 문서가 약속하지 않으니, 처음 나온 차례를 목록에 따로 적는다        var counts = new Dictionary<string, int>();        var order = new List<string>();        foreach (string code in Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries))        {            if (counts.TryGetValue(code, out int c))            {                counts[code] = c + 1;            }            else            {                counts[code] = 1;                order.Add(code);            }        }        var sb = new StringBuilder();        foreach (string code in order) sb.Append(code).Append(' ').Append(counts[code]).Append('\n');        Console.Write(sb);    }}
C++main.cpp · 19 行
#include <iostream>#include <string>#include <unordered_map>#include <vector> int main() {    std::ios::sync_with_stdio(false);    int n;    std::cin >> n;    // std::map은 열쇠 순으로, unordered_map은 뒤섞인 차례로 돈다. 처음 나온 차례는 따로 적는다    std::unordered_map<std::string, int> counts;    std::vector<std::string> order;    for (int i = 0; i < n; i++) {        std::string code;        std::cin >> code;        if (counts[code]++ == 0) order.push_back(code);    }    for (const std::string& code : order) std::cout << code << " " << counts[code] << "\n";}
Cmain.c · 25 行
#include <stdio.h>#include <string.h> /* C에는 사전이 없어서, 처음 나온 차례대로 코드와 개수를 배열에 적고 찾을 때는 앞에서부터 훑는다 */static char names[5000][9];static int counts[5000]; int main(void) {    int n, kinds = 0;    char code[16];    scanf("%d", &n);    for (int i = 0; i < n; i++) {        scanf("%15s", code);        int j = 0;        while (j < kinds && strcmp(names[j], code) != 0) j++;        if (j == kinds) {            strcpy(names[kinds], code);            counts[kinds] = 0;            kinds++;        }        counts[j]++;    }    for (int j = 0; j < kinds; j++) printf("%s %d\n", names[j], counts[j]);    return 0;}
Craykonmain.cray · 16 行
n = コンソール.行読み().数に();codes = コンソール.行読み().空白除去().分ける(" ");counts = 辞書();それぞれ (code の中 codes){    もし (!counts.含む(code))    {        counts[code] = 0;    }    counts[code] = counts[code] + 1;}// 크레이콘 사전의 열쇠목록은 처음 넣은 차례다それぞれ (code の中 counts.キー一覧){    コンソール.書く(code + " " + counts[code]);}