{ }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.连接(" "));            }            否则            {                控制台.输出("-");            }            跳出;    }}