{ }codelper
목록 복사목록에서 빼기사전 찾기P0107

저장 칸에 목록 저장하고 불러오기

낱말을 차례로 담는 목록이 하나 있고, 처음에는 비어 있다.
게임의 저장 칸처럼, 지금 목록을 이름 붙인 칸에 저장해 두었다가 나중에 불러올 수 있다.
첫 줄에 명령 수 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
입력(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 sys lines = 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.잇기(" "));            }            아니면            {                콘솔.쓰기("-");            }            그만;    }}