{ }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()・標準入力に渡されます(実行中に入力を求めません)。

$ 準備完了

コードを入力して解くにはパソコンで開いてください。モバイルでは問題と解答を見られます。

ほかの言語では?言語別の解答を見る · 8 言語の解答

要点は保存するときも読み込むときもコピーすることです。
Python・JS・TS・Java・C#・Craykon のリストは参照なので、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)、Craykon doc.部分リスト(0, doc.個数)(韓国語 doc.부분목록(0, doc.개수))です。
C++ の vector は値なので slots[s] = doc が自動でコピーし、C は配列を構造体に入れれば構造体の代入がまるごとコピーしてくれます(裸の配列は代入できません)。
二つ目の落とし穴は値での取り除きです。
Python remove はないと ValueError なので先に in で確かめ、Java remove(Object)・C# Remove・Craykon 取り除く(韓国語 빼기)はないと false を返すだけです。
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.つなぐ(" "));            }            でなければ            {                コンソール.書く("-");            }            抜ける;    }}