{ }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 的非负整数的键,不管插入顺序如何,都会按从小到大排在最前面("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]);}