相册里的每张照片都贴着一个标签。 相册为每个标签记住贴着这个标签的照片数。 至少贴在一张照片上的标签叫做存在的标签,一开始没有存在的标签。 依次处理 q 条命令。 add t k 表示贴着标签 t 的照片增加 k 张。 如果标签 t 原来不存在,就新建它。 然后输出标签 t 的照片数。 move a b 表示把贴着标签 a 的照片全部改贴标签 b。 如果标签 a 不存在,什么都不改变,输出 0。 如果标签 a 存在并且 a 与 b 相同,什么都不改变,输出这个标签的照片数。 如果标签 a 存在并且 a 与 b 不同,把 a 的照片数加到 b 的照片数上,并去掉标签 a。 如果标签 b 原来不存在,就新建它。 然后输出标签 b 的照片数。 处理完所有命令后,输出存在的标签的个数。
输入
第一行给出命令的条数 q(1 ≤ q ≤ 2,000)。 接下来 q 行每行给出一条命令。 命令的形式是 add t k 或 move a b,词与词之间用一个空格隔开。 标签名 t、a、b 是只由英文小写字母组成的字符串,长度为 1 到 10。 k 是整数(1 ≤ k ≤ 1,000)。
8
add cat 3
add dog 2
move cat pet
move dog pet
move bird dog
add cat 4
move cat cat
move pet cat
stdout
3
2
3
5
0
4
4
9
1
main.py
错误列表00F8 ↓ · ⇧F8 ↑
输入(stdin)
运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。
$ 就绪
请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。
其他语言怎么写?查看各语言题解 · 8 种语言题解
从字典里删除键的同时取出它的值的方法,以及往可能不存在的键上加值的方法,各语言并不相同。 Python 的 moved = tags.pop(a) 在删除的同时返回值,再用 tags[b] = tags.get(b, 0) + moved 相加。 Java 的 tags.remove(a) 返回被删除的值(不存在时为 null),tags.merge(b, moved, Integer::sum) 在键不存在时放入、存在时相加。 C# 用 tags.Remove(a, out int moved) 接收值。 相加时先用 TryGetValue 取出(不存在时为 0)。 JavaScript 和 TypeScript 的 Map 的 delete 返回的不是值而是 true/false,所以先用 get 读出来,再用 (tags.get(b) ?? 0) + moved 相加。 C++ 从 find 找到的位置读出值,再用 erase 删除。 tags[b] += moved 会把不存在的键建成 0,所以可以直接用。 C 没有字典,所以把名字数组和数量数组并排放着,从前往后查找。 Craykon 中移除键的操作返回的不是值而是真假,所以要先读出来;不存在的键不能读,所以要先确认键是否存在,不存在就先放入 0,然后再相加。 陷阱一:移到同一个标签。 示例 1 的第七条命令 move cat cat 输出 4,什么都不改变。 如果不检查 a 和 b 是否相同就“加到 b 上再删除 a”,cat 会先变成 8 然后被整个删除,于是这一行输出 8,下一行输出的不是 9 而是 5。 陷阱二:覆盖。 第四条命令 move dog pet 中 pet 已经有 3 张,所以变成 5。 如果不相加而用 Java 的 put 放入,会得到 2。 陷阱三:只是询问就产生的键。 在 C++ 中用 tags[a] == 0 来问标签是否存在,会把不存在的键建成 0。 第五条命令 move bird dog 之后留下一个 0 张照片的 bird,最后一行不是 1 而是 2。 陷阱四:从不存在的标签取值。 同样在第五条命令,不检查就调用 Python 的 tags.pop(a) 会因 KeyError 而停止。 陷阱五:往不存在的键上加值。 第三条命令 move cat pet 中 pet 还不存在。 在 C# 中写 tags[b] += moved 会因 KeyNotFoundException 而停止。
Pythonmain.py · 24 行
import syslines = sys.stdin.read().split("\n")q = int(lines[0])tags = {}out = []for i in range(1, q + 1): parts = lines[i].split() if parts[0] == "add": t = parts[1] tags[t] = tags.get(t, 0) + int(parts[2]) out.append(str(tags[t])) else: a, b = parts[1], parts[2] if a not in tags: out.append("0") elif a == b: out.append(str(tags[a])) else: moved = tags.pop(a) # 지우면서 값을 돌려받는다 tags[b] = tags.get(b, 0) + moved out.append(str(tags[b]))out.append(str(len(tags)))print("\n".join(out))
JavaScriptmain.js · 27 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const q = Number(lines[0]);const tags = new Map();const out = [];for (let i = 1; i <= q; i++) { const parts = lines[i].trim().split(" "); if (parts[0] === "add") { const t = parts[1]; tags.set(t, (tags.get(t) ?? 0) + Number(parts[2])); out.push(String(tags.get(t))); } else { const a = parts[1]; const b = parts[2]; if (!tags.has(a)) { out.push("0"); } else if (a === b) { out.push(String(tags.get(a))); } else { const moved = tags.get(a); // delete는 값이 아니라 true/false를 돌려준다 tags.delete(a); tags.set(b, (tags.get(b) ?? 0) + moved); out.push(String(tags.get(b))); } }}out.push(String(tags.size));console.log(out.join("\n"));
TypeScriptmain.ts · 31 行
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const q: number = Number(lines[0]);const tags: Map<string, number> = new Map();const out: string[] = [];for (let i = 1; i <= q; i++) { const parts: string[] = lines[i].trim().split(" "); if (parts[0] === "add") { const t: string = parts[1]; const count: number = (tags.get(t) ?? 0) + Number(parts[2]); tags.set(t, count); out.push(String(count)); } else { const a: string = parts[1]; const b: string = parts[2]; const moved: number | undefined = tags.get(a); // 없으면 undefined if (moved === undefined) { out.push("0"); } else if (a === b) { out.push(String(moved)); } else { tags.delete(a); // delete는 값이 아니라 true/false를 돌려준다 const count: number = (tags.get(b) ?? 0) + moved; tags.set(b, count); out.push(String(count)); } }}out.push(String(tags.size));console.log(out.join("\n"));
JavaMain.java · 36 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.HashMap;import java.util.Map;public class Main { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int q = Integer.parseInt(br.readLine().trim()); Map<String, Integer> tags = new HashMap<>(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < q; i++) { String[] parts = br.readLine().trim().split(" "); if (parts[0].equals("add")) { // merge: 없으면 넣고, 있으면 더한 값을 넣는다. 넣은 값을 돌려준다 int count = tags.merge(parts[1], Integer.parseInt(parts[2]), Integer::sum); sb.append(count).append('\n'); } else { String a = parts[1]; String b = parts[2]; if (!tags.containsKey(a)) { sb.append(0).append('\n'); } else if (a.equals(b)) { sb.append(tags.get(a)).append('\n'); } else { int moved = tags.remove(a); // 지우면서 값을 돌려받는다 int count = tags.merge(b, moved, Integer::sum); sb.append(count).append('\n'); } } } sb.append(tags.size()).append('\n'); System.out.print(sb); }}
C#Main.cs · 48 行
using System;using System.Collections.Generic;using System.Text;class Program{ static void Main() { int q = int.Parse(Console.ReadLine().Trim()); var tags = new Dictionary<string, int>(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < q; i++) { string[] parts = Console.ReadLine().Trim().Split(' '); if (parts[0] == "add") { // 없는 열쇠에 +=를 하면 예외가 난다: TryGetValue로 먼저 꺼낸다(없으면 0) tags.TryGetValue(parts[1], out int count); count += int.Parse(parts[2]); tags[parts[1]] = count; sb.Append(count).Append('\n'); } else { string a = parts[1]; string b = parts[2]; if (!tags.ContainsKey(a)) { sb.Append(0).Append('\n'); } else if (a == b) { sb.Append(tags[a]).Append('\n'); } else { tags.Remove(a, out int moved); // 지우면서 값을 돌려받는다 tags.TryGetValue(b, out int count); count += moved; tags[b] = count; sb.Append(count).Append('\n'); } } } sb.Append(tags.Count).Append('\n'); Console.Write(sb); }}
C++main.cpp · 39 行
#include <iostream>#include <string>#include <unordered_map>using namespace std;int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int q; cin >> q; unordered_map<string, int> tags; for (int i = 0; i < q; i++) { string op; cin >> op; if (op == "add") { string t; int k; cin >> t >> k; tags[t] += k; // []는 없는 열쇠를 0으로 만들어 준다 cout << tags[t] << '\n'; } else { string a, b; cin >> a >> b; auto it = tags.find(a); // find는 열쇠를 만들지 않는다 if (it == tags.end()) { cout << 0 << '\n'; } else if (a == b) { cout << it->second << '\n'; } else { int moved = it->second; tags.erase(it); tags[b] += moved; cout << tags[b] << '\n'; } } } cout << tags.size() << '\n'; return 0;}
Cmain.c · 62 行
#include <stdio.h>#include <string.h>/* C에는 사전이 없다: 이름 배열과 수 배열을 나란히 두고 앞에서부터 찾는다 */static char names[2000][11];static int counts[2000];static int size = 0;static int find(const char *t) { for (int i = 0; i < size; i++) { if (strcmp(names[i], t) == 0) return i; } return -1;}/* 없으면 0장으로 새로 만들고 그 자리를 돌려준다 */static int find_or_add(const char *t) { int i = find(t); if (i < 0) { i = size++; strcpy(names[i], t); counts[i] = 0; } return i;}int main(void) { int q; if (scanf("%d", &q) != 1) return 1; for (int i = 0; i < q; i++) { char op[8], a[11], b[11]; if (scanf("%7s", op) != 1) return 1; if (strcmp(op, "add") == 0) { int k; if (scanf("%10s %d", a, &k) != 2) return 1; int at = find_or_add(a); counts[at] += k; printf("%d\n", counts[at]); } else { if (scanf("%10s %10s", a, b) != 2) return 1; int from = find(a); if (from < 0) { printf("0\n"); } else if (strcmp(a, b) == 0) { printf("%d\n", counts[from]); } else { int moved = counts[from]; /* 지우기: 마지막 칸을 빈 자리로 옮긴다 */ size--; if (from != size) { strcpy(names[from], names[size]); counts[from] = counts[size]; } int to = find_or_add(b); counts[to] += moved; printf("%d\n", counts[to]); } } } printf("%d\n", size); return 0;}
Craykonmain.cray · 42 行
명령수 = 控制台.读行().转数字();꼬리표들 = 字典();循环 (i = 0; i < 명령수; i++){ 칸들 = 控制台.读行().去空白().分割(); 如果 (칸들[0] == "add") { 이름 = 칸들[1]; // 없는 열쇠를 읽으면 멈춘다: 있는지 먼저 본다 如果 (!꼬리표들.包含(이름)) { 꼬리표들[이름] = 0; } 꼬리표들[이름] += 칸들[2].转数字(); 控制台.输出(꼬리표들[이름]); } 否则 { 원래 = 칸들[1]; 새것 = 칸들[2]; 如果 (!꼬리표들.包含(원래)) { 控制台.输出(0); } 否则 如果 (원래 == 새것) { 控制台.输出(꼬리표들[원래]); } 否则 { 옮길수 = 꼬리표들[원래]; // 빼기는 값이 아니라 참/거짓을 돌려준다 꼬리표들.移除(원래); 如果 (!꼬리표들.包含(새것)) { 꼬리표들[새것] = 0; } 꼬리표들[새것] += 옮길수; 控制台.输出(꼬리표들[새것]); } }}控制台.输出(꼬리표들.数量);