{ }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()·표준 입력으로 들어가요(실행 중에 묻지 않아요).

$ 준비됐어요

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

다른 언어로는?언어별 풀이 보기 · 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으로 생겨요), 크레이콘 사전()과 있나예요.
이 문제의 함정은 돌 때의 차례예요.
Python dict(3.7부터), JS·TS Map, Java LinkedHashMap, 크레이콘 사전의 열쇠목록은 처음 넣은 차례를 지켜요.
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]);}