アルバムの写真にはタグを一つずつ付ける。 アルバムはタグごとに、そのタグが付いた写真の枚数を覚えている。 写真が 1 枚以上付いているタグを「あるタグ」と呼び、最初はあるタグが一つもない。 命令 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 の写真の枚数を出力する。 すべての命令を処理したあと、あるタグの数を出力する。
入力
1 行目に命令の数 q が与えられる(1 ≤ q ≤ 2,000)。 続く q 行に命令が 1 行に一つずつ与えられる。 命令は 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; } 꼬리표들[새것] += 옮길수; コンソール.書く(꼬리표들[새것]); } }}コンソール.書く(꼬리표들.個数);