낱말을 차례로 담는 목록이 하나 있고, 처음에는 비어 있다. 게임의 저장 칸처럼, 지금 목록을 이름 붙인 칸에 저장해 두었다가 나중에 불러올 수 있다. 첫 줄에 명령 수 q가, 다음 q줄에 명령이 하나씩 주어진다. add w는 목록 끝에 낱말 w를 넣는다. remove w는 목록에서 맨 앞에 나오는 w 하나를 뺀다. 목록에 w가 없으면 아무 일도 하지 않는다. save s는 지금 목록의 모습을 이름이 s인 칸에 저장한다. 그 칸에 이미 저장한 것이 있으면 새것으로 바꾼다. load s는 목록을 칸 s에 저장했던 모습으로 되돌린다. 칸 s에 저장한 적이 없으면 아무 일도 하지 않는다. 저장한 칸의 내용은 다음에 그 칸에 save할 때만 바뀐다. 불러온 뒤에 목록을 고쳐도 칸의 내용은 그대로다. print가 나올 때마다 목록의 낱말을 빈칸 하나씩으로 나눠 한 줄에 출력한다. 목록이 비어 있으면 -를 출력한다.
입력
1 ≤ q ≤ 2000. 낱말과 칸 이름은 영어 소문자·숫자 1 ~ 10글자. 출력 전체는 100,000글자를 넘지 않는다
출력
print마다 한 줄
예시 1 · stdin
10
add red
add blue
save s1
remove red
add green
print
load s1
add pink
load s1
print
stdout
blue green
red blue
예시 2 · stdin
7
remove x
print
load nothing
add x
add x
remove x
print
stdout
-
x
main.py
오류 목록00F8 ↓ · ⇧F8 ↑
입력(stdin)
실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).
$ 준비됐어요
코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.
다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이
핵심은 저장할 때와 불러올 때 둘 다 복사하는 것이에요. Python·JS·TS·Java·C#·크레이콘의 목록은 참조라서 slots[s] = doc은 같은 목록을 하나 더 가리킬 뿐이고, 그 뒤에 doc을 고치면 칸의 내용도 바뀌어요. 불러올 때 doc = slots[s]도 마찬가지로, 불러온 뒤 add 하면 칸이 망가져서 두 번째로 불러올 때 드러나요. 복사는 Python doc.copy()(또는 doc[:]·list(doc)), JS·TS [...doc](또는 doc.slice()), Java new ArrayList<>(doc), C# new List<string>(doc), 크레이콘 doc.부분목록(0, doc.개수)예요. C++ vector는 값이라 slots[s] = doc이 저절로 복사하고, C는 배열을 구조체에 넣으면 구조체 대입이 통째로 복사해 줘요(맨 배열은 대입할 수 없어요). 두 번째 함정은 값으로 빼기예요. Python remove는 없으면 ValueError라 먼저 in으로 묻고, Java remove(Object)·C# Remove·크레이콘 빼기는 없으면 거짓을 돌려줄 뿐이에요. JS에는 값으로 빼는 메서드가 없어서 splice(indexOf(w), 1)을 쓰는데, 없으면 indexOf가 -1이고 splice(-1, 1)은 마지막 낱말을 빼요. 모두 맨 앞의 것 하나만 빼요.
Pythonmain.py · 24 줄
import syslines = sys.stdin.read().split("\n")q = int(lines[0])doc = []slots = {} # 이름 → 저장해 둔 목록out = []for line in lines[1:q + 1]: cmd, *arg = line.split() if cmd == "add": doc.append(arg[0]) elif cmd == "remove": if arg[0] in doc: # 없으면 remove가 ValueError를 낸다 doc.remove(arg[0]) # 맨 앞의 것 하나만 뺀다 elif cmd == "save": slots[arg[0]] = doc.copy() # 복사해 둬야 나중에 doc을 고쳐도 그대로다 elif cmd == "load": if arg[0] in slots: doc = slots[arg[0]].copy() # 불러올 때도 복사 — 아니면 고칠 때 저장한 것도 바뀐다 else: out.append(" ".join(doc) if doc else "-")print("\n".join(out))
JavaScriptmain.js · 22 줄
const lines = require("fs").readFileSync(0, "utf8").split("\n");const q = Number(lines[0]);let doc = [];const slots = new Map(); // 이름 → 저장해 둔 배열const out = [];for (const line of lines.slice(1, q + 1)) { const [cmd, w] = line.trim().split(" "); if (cmd === "add") { doc.push(w); } else if (cmd === "remove") { const at = doc.indexOf(w); if (at !== -1) doc.splice(at, 1); // -1을 splice에 넣으면 마지막 것이 빠진다 } else if (cmd === "save") { slots.set(w, [...doc]); // 복사해 둔다 } else if (cmd === "load") { if (slots.has(w)) doc = [...slots.get(w)]; // 불러올 때도 복사 } else { out.push(doc.length > 0 ? doc.join(" ") : "-"); }}console.log(out.join("\n"));
TypeScriptmain.ts · 24 줄
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const q = Number(lines[0]);let doc: string[] = [];const slots = new Map<string, string[]>(); // 이름 → 저장해 둔 배열const out: string[] = [];for (const line of lines.slice(1, q + 1)) { const [cmd, w] = line.trim().split(" "); if (cmd === "add") { doc.push(w); } else if (cmd === "remove") { const at = doc.indexOf(w); if (at !== -1) doc.splice(at, 1); // -1을 splice에 넣으면 마지막 것이 빠진다 } else if (cmd === "save") { slots.set(w, [...doc]); // 복사해 둔다 } else if (cmd === "load") { const saved = slots.get(w); if (saved !== undefined) doc = [...saved]; // 불러올 때도 복사 } else { out.push(doc.length > 0 ? doc.join(" ") : "-"); }}console.log(out.join("\n"));
JavaMain.java · 26 줄
import java.io.*;import java.util.*;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()); List<String> doc = new ArrayList<>(); Map<String, List<String>> slots = new HashMap<>(); // 이름 → 저장해 둔 목록 StringBuilder out = new StringBuilder(); for (int i = 0; i < q; i++) { String[] p = br.readLine().trim().split(" "); switch (p[0]) { case "add" -> doc.add(p[1]); case "remove" -> doc.remove(p[1]); // remove(Object): 맨 앞의 것 하나, 없으면 false case "save" -> slots.put(p[1], new ArrayList<>(doc)); // 복사해 둔다 case "load" -> { if (slots.containsKey(p[1])) doc = new ArrayList<>(slots.get(p[1])); // 불러올 때도 복사 } default -> out.append(doc.isEmpty() ? "-" : String.join(" ", doc)).append('\n'); } } System.out.print(out); }}
C#Main.cs · 38 줄
using System;using System.Collections.Generic;using System.Text;class Program{ static void Main() { int q = int.Parse(Console.ReadLine().Trim()); var doc = new List<string>(); var slots = new Dictionary<string, List<string>>(); // 이름 → 저장해 둔 목록 var sb = new StringBuilder(); for (int i = 0; i < q; i++) { string[] p = Console.ReadLine().Trim().Split(' '); switch (p[0]) { case "add": doc.Add(p[1]); break; case "remove": doc.Remove(p[1]); // 맨 앞의 것 하나, 없으면 false break; case "save": slots[p[1]] = new List<string>(doc); // 복사해 둔다 break; case "load": if (slots.TryGetValue(p[1], out var saved)) doc = new List<string>(saved); // 불러올 때도 복사 break; default: sb.Append(doc.Count > 0 ? string.Join(" ", doc) : "-").Append('\n'); break; } } Console.Write(sb); }}
C++main.cpp · 35 줄
#include <algorithm>#include <iostream>#include <map>#include <string>#include <vector>int main() { int q; std::cin >> q; std::vector<std::string> doc; std::map<std::string, std::vector<std::string>> slots; // 이름 → 저장해 둔 목록 std::string out; for (int i = 0; i < q; i++) { std::string cmd, w; std::cin >> cmd; if (cmd != "print") std::cin >> w; if (cmd == "add") { doc.push_back(w); } else if (cmd == "remove") { auto it = std::find(doc.begin(), doc.end(), w); if (it != doc.end()) doc.erase(it); // end()를 지우면 정의되지 않은 동작 } else if (cmd == "save") { slots[w] = doc; // vector는 값이라 대입이 곧 복사 } else if (cmd == "load") { auto it = slots.find(w); if (it != slots.end()) doc = it->second; // 여기서도 복사 } else { if (doc.empty()) out += "-"; for (size_t j = 0; j < doc.size(); j++) out += (j ? " " : "") + doc[j]; out += "\n"; } } std::cout << out;}
Cmain.c · 66 줄
#include <stdio.h>#include <string.h>#define CAP 2000 /* 명령이 많아야 2000개라 목록도 이름도 2000개를 넘지 않는다 *//* 목록 = 낱말 번호 배열과 길이. 구조체 안의 배열은 대입하면 통째로 복사된다 */typedef struct { int len; int w[CAP];} Doc;static char words[CAP][11], slot_names[CAP][11];static int word_count = 0, slot_count = 0;static Doc doc, slots[CAP];static int word_id(const char *s) { for (int i = 0; i < word_count; i++) if (strcmp(words[i], s) == 0) return i; strcpy(words[word_count], s); return word_count++;}static int find_slot(const char *s) { for (int i = 0; i < slot_count; i++) if (strcmp(slot_names[i], s) == 0) return i; return -1;}int main(void) { int q; char cmd[8], w[11]; scanf("%d", &q); for (int i = 0; i < q; i++) { scanf("%7s", cmd); if (strcmp(cmd, "print") == 0) { if (doc.len == 0) printf("-"); for (int j = 0; j < doc.len; j++) printf(j ? " %s" : "%s", words[doc.w[j]]); printf("\n"); continue; } scanf("%10s", w); if (strcmp(cmd, "add") == 0) { doc.w[doc.len++] = word_id(w); } else if (strcmp(cmd, "remove") == 0) { int id = word_id(w); for (int j = 0; j < doc.len; j++) { if (doc.w[j] == id) { /* 맨 앞의 것을 빼고 뒤를 한 칸씩 당긴다 */ memmove(doc.w + j, doc.w + j + 1, sizeof(int) * (doc.len - j - 1)); doc.len--; break; } } } else if (strcmp(cmd, "save") == 0) { int s = find_slot(w); if (s < 0) { s = slot_count++; strcpy(slot_names[s], w); } slots[s] = doc; /* 구조체 대입 = 복사 */ } else { int s = find_slot(w); if (s >= 0) doc = slots[s]; } } return 0;}
Craykonmain.cray · 36 줄
q = 콘솔.줄읽기().수로변환();doc = [];slots = 사전(); // 이름 → 저장해 둔 목록반복 (i = 0; i < q; i++){ p = 콘솔.줄읽기().공백제거().나누기(); 선택 (p[0]) { 경우 "add": doc.넣기(p[1]); 그만; 경우 "remove": doc.빼기(p[1]); // 맨 앞의 것 하나, 없으면 거짓 그만; 경우 "save": slots[p[1]] = doc.부분목록(0, doc.개수); // 복사해 둔다 그만; 경우 "load": 만약 (slots.있나(p[1])) { doc = slots[p[1]].부분목록(0, slots[p[1]].개수); // 불러올 때도 복사 } 그만; 그밖에: 만약 (doc.개수 > 0) { 콘솔.쓰기(doc.잇기(" ")); } 아니면 { 콘솔.쓰기("-"); } 그만; }}