Each photo in a photo album carries one tag.
For each tag, the album remembers the number of photos carrying it.
A tag carried by at least one photo is called an existing tag, and at first there are no existing tags.
Process q commands in order.
add t k adds k photos carrying tag t. If tag t did not exist, it is created. Then print the number of photos of tag t.
move a b changes the tag of every photo carrying tag a to tag b.
If tag a does not exist, nothing changes and you print 0.
If tag a exists and a and b are the same, nothing changes and you print the number of photos of that tag.
If tag a exists and a and b are different, add the number of photos of a to the number of photos of b and delete tag a. If tag b did not exist, it is created. Then print the number of photos of tag b.
After all commands are processed, print the number of existing tags.
Input
The first line contains the number of commands q (1 ≤ q ≤ 2,000).
Each of the next q lines contains one command.
A command has the form add t k or move a b, with single spaces between the words.
The tag names t, a and b are strings of length 1 to 10 made only of lowercase letters.
k is an integer (1 ≤ k ≤ 1,000).
Output
For each command, print the number defined above, one per line in the order the commands are given.
On the last line, print the number of existing tags after all commands are processed.
Example 1 · stdin
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
Error List00F8 ↓ · ⇧F8 ↑
Input (stdin)
When you run, these lines are fed to input()/stdin in order (the program doesn't stop to ask).
$ Ready
Open this on a computer to write and run code. On mobile you can read the problem and the solutions.
In other languagesShow solutions by language · 8 languages
Languages differ in how you take a value out of a dictionary while deleting its key, and in how you add to a key that may not be there.
In Python, moved = tags.pop(a) deletes and returns the value, and tags[b] = tags.get(b, 0) + moved adds it.
In Java, tags.remove(a) returns the removed value (null if absent), and tags.merge(b, moved, Integer::sum) inserts if absent and adds if present.
In C#, tags.Remove(a, out int moved) hands you the value. To add, you first fetch with TryGetValue (0 if absent).
In JavaScript and TypeScript, a Map's delete returns true or false, not the value, so you read with get first and add with (tags.get(b) ?? 0) + moved.
In C++, you read the value at the position find gives you and delete with erase. tags[b] += moved creates a missing key as 0, so it can be used as is.
C has no dictionary, so you keep an array of names and an array of counts side by side and search from the front.
In Craykon, removing a key returns true or false, not the value, so you read first; a missing key cannot be read, so you check that the key is there, put 0 if it is not, and then add.
Trap one: moving to the same tag. The seventh command of example 1, move cat cat, prints 4 and changes nothing. If you "add to b and delete a" without checking whether a and b are the same, cat becomes 8 and is then deleted entirely, so that line shows 8 and the next line shows 5 instead of 9.
Trap two: overwriting. In the fourth command, move dog pet, pet already has 3 photos, so it becomes 5. Storing with Java's put instead of adding gives 2.
Trap three: a key created just by asking. In C++, asking whether a tag exists with tags[a] == 0 creates the missing key as 0. After the fifth command, move bird dog, a bird tag with 0 photos stays behind, so the last line is 2 instead of 1.
Trap four: taking from a tag that does not exist. On the same fifth command, calling Python's tags.pop(a) without checking stops with a KeyError.
Trap five: adding to a missing key. In the third command, move cat pet, pet does not exist yet. In C#, tags[b] += moved stops with a KeyNotFoundException.
Pythonmain.py · 24 lines
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 lines
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 lines
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 lines
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 lines
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 lines
#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 lines
#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 lines
명령수 = Console.ReadLine().ToNumber();꼬리표들 = Dict();for (i = 0; i < 명령수; i++){ 칸들 = Console.ReadLine().Trim().Split(); if (칸들[0] == "add") { 이름 = 칸들[1]; // 없는 열쇠를 읽으면 멈춘다: 있는지 먼저 본다 if (!꼬리표들.Contains(이름)) { 꼬리표들[이름] = 0; } 꼬리표들[이름] += 칸들[2].ToNumber(); Console.WriteLine(꼬리표들[이름]); } else { 원래 = 칸들[1]; 새것 = 칸들[2]; if (!꼬리표들.Contains(원래)) { Console.WriteLine(0); } else if (원래 == 새것) { Console.WriteLine(꼬리표들[원래]); } else { 옮길수 = 꼬리표들[원래]; // 빼기는 값이 아니라 참/거짓을 돌려준다 꼬리표들.Remove(원래); if (!꼬리표들.Contains(새것)) { 꼬리표들[새것] = 0; } 꼬리표들[새것] += 옮길수; Console.WriteLine(꼬리표들[새것]); } }}Console.WriteLine(꼬리표들.Count);