{ }codelper
リストの切り出しリストのコピーリストからの削除P0298

コピーして切り取って貼り付け

n 個の整数が入ったリストと、最初は空のクリップボードがある。
q 個の命令を与えられた順に処理する。
copy l r は今のリストの l 番目から r 番目まで(両端を含む)をクリップボードにコピーする。
リストは変わらない。
cut l r は今のリストの l 番目から r 番目まで(両端を含む)をクリップボードに移し、リストから取り除く。
copy と cut はクリップボードに前からあったものを消して新しい断片に置き換える。
paste p はクリップボードの中身を順番そのままに、今のリストの p 番目の数のすぐ後ろに挿入する。
p が 0 なら先頭に入れる。
貼り付けてもクリップボードはそのまま残る。
空のクリップボードを貼り付けても何も起こらない。
貼り付けたあとのリストの個数が M を超えることになる paste は無視する(ちょうど M 個になるものは貼り付ける)。
番号は先頭の数を 1 として数える。
一行目に n と M、二行目に n 個の整数、三行目に q、続く q 行に命令が一つずつ与えられる。
一行の中の値は空白一つで区切られる。
すべての命令を終えたあと、一行目にリストの個数を、二行目にリストの数を順に空白一つで区切って出力せよ。
リストが空なら二行目に - を出力する。

入力
1 ≤ n ≤ 1,000、n ≤ M ≤ 5,000、1 ≤ q ≤ 500(カンマなしの数字だけで与えられる)。
リストの数は -999 以上 999 以下。
copy と cut は、そのときのリストの個数を len として 1 ≤ l ≤ r ≤ len(リストが空のときは与えられない)。
paste は 0 ≤ p ≤ len
出力
一行目にリストの個数、二行目にリストの数(空白一つ区切り)または -
例 1 · stdin
5 8
10 20 30 40 50
7
copy 2 3
paste 5
cut 1 2
paste 0
paste 3
cut 4 7
paste 1
stdout
7
10 40 50 20 30 20 30
main.py
入力(stdin)

実行すると、これらの行が順に input()・標準入力に渡されます(実行中に入力を求めません)。

$ 準備完了

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

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

断片の指し方が「開始と終わり」の言語と「開始と個数」の言語に分かれます。
開始と終わり(終わりは含まない):Python a[l-1:r]、JS・TS a.slice(l - 1, r)、Java a.subList(l - 1, r)、C++ はイテレータ二つ a.begin() + (l - 1), a.begin() + r。
開始と個数:C# a.GetRange(l - 1, r - l + 1)、Craykon a.部分リスト(l - 1, r - l + 1)、C memcpy(clip, a + l - 1, 個数 * sizeof(int))。
同じ JS の中でも slice(開始, 終わり) と splice(開始, 個数) が違います。
取り除く:Python del a[l-1:r]、JS・TS a.splice(l - 1, 個数)(取り除いたものを返すので cut が一行)、Java a.subList(l - 1, r).clear()、C# RemoveRange(開始, 個数)、C++ a.erase(最初, 終わり)、C memmove。
挿入:Python a[p:p] = clip、JS・TS a.splice(p, 0, ...clip)、Java a.addAll(p, clip)、C# a.InsertRange(p, clip)、C++ a.insert(位置, 最初, 終わり)、C は memmove で後ろをずらして memcpy。
Craykon には複数をまとめて取り除いたり挿入したりする機能がないので、前の断片 部分リスト(0, p) にクリップボードと後ろの断片を 追加 でつないで新しいリストを作ります。
落とし穴 ① 終わりと個数の取り違え:slice(l - 1, r - l + 1) や GetRange(l - 1, r)。
JS と Python は範囲を外れてもエラーなしで短くして返し、C#・Java・Craykon はエラーで止まります。
② コピーか窓か:Python のスライス、JS の slice、C# の GetRange、Craykon の 部分リスト は新しいリストを返しますが、Java の subList は元のリストをのぞく窓です。
窓をそのままクリップボードにすると、リストに挿入したり取り除いたりしたあとでその窓をもう一度使うときに ConcurrentModificationException になります。
new ArrayList<>(a.subList(l - 1, r)) でコピーを作ります。
③「p 番目の後ろ」は 0 から数えた p 番の位置です。
p - 1 と書くと p = 0 のとき Python では a[-1:-1] になり、最後の数の前に入ります。
④ 上限は貼り付けたあとの個数 今の個数 + クリップボードの個数 で比べ、ちょうど M は貼り付けます(<=)。
⑤ cut もクリップボードを新しい断片に置き換えます。
⑥ r 番目も断片に入ります。

Pythonmain.py · 24 行
import sys lines = sys.stdin.read().split("\n")n, limit = map(int, lines[0].split())a = list(map(int, lines[1].split()))q = int(lines[2])clip = []  # 오려 두기 판 for line in lines[3:3 + q]:    p = line.split()    if p[0] == "copy":        l, r = int(p[1]), int(p[2])        clip = a[l - 1:r]  # 조각은 새 목록(사본)이다    elif p[0] == "cut":        l, r = int(p[1]), int(p[2])        clip = a[l - 1:r]        del a[l - 1:r]    else:        at = int(p[1])        if len(a) + len(clip) <= limit:            a[at:at] = clip  # 빈 조각 자리에 넣으면 끼워 넣기가 된다 print(len(a))print(" ".join(map(str, a)) if a else "-")
JavaScriptmain.js · 22 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const limit = Number(lines[0].trim().split(" ")[1]);const a = lines[1].trim().split(" ").map(Number);const q = Number(lines[2]);let clip = []; // 오려 두기 판 for (const line of lines.slice(3, 3 + q)) {  const p = line.trim().split(" ");  if (p[0] === "copy") {    const l = Number(p[1]), r = Number(p[2]);    clip = a.slice(l - 1, r); // slice(시작, 끝): 끝은 들어가지 않는다. 사본을 준다  } else if (p[0] === "cut") {    const l = Number(p[1]), r = Number(p[2]);    clip = a.splice(l - 1, r - l + 1); // splice(시작, 개수): 뺀 것들을 돌려준다  } else {    const at = Number(p[1]);    if (a.length + clip.length <= limit) a.splice(at, 0, ...clip);  }} console.log(a.length);console.log(a.length > 0 ? a.join(" ") : "-");
TypeScriptmain.ts · 24 行
import { readFileSync } from "node:fs"; const lines: string[] = readFileSync(0, "utf8").split("\n");const limit = Number(lines[0].trim().split(" ")[1]);const a: number[] = lines[1].trim().split(" ").map(Number);const q = Number(lines[2]);let clip: number[] = []; // 오려 두기 판 for (const line of lines.slice(3, 3 + q)) {  const p = line.trim().split(" ");  if (p[0] === "copy") {    const l = Number(p[1]), r = Number(p[2]);    clip = a.slice(l - 1, r); // slice(시작, 끝): 끝은 들어가지 않는다. 사본을 준다  } else if (p[0] === "cut") {    const l = Number(p[1]), r = Number(p[2]);    clip = a.splice(l - 1, r - l + 1); // splice(시작, 개수): 뺀 것들을 돌려준다  } else {    const at = Number(p[1]);    if (a.length + clip.length <= limit) a.splice(at, 0, ...clip);  }} console.log(a.length);console.log(a.length > 0 ? a.join(" ") : "-");
JavaMain.java · 43 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.ArrayList;import java.util.List;import java.util.StringTokenizer; public class Main {    public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        int limit = Integer.parseInt(in.readLine().trim().split(" ")[1]);        List<Integer> a = new ArrayList<>();        StringTokenizer st = new StringTokenizer(in.readLine());        while (st.hasMoreTokens()) a.add(Integer.parseInt(st.nextToken()));        int q = Integer.parseInt(in.readLine().trim());        List<Integer> clip = new ArrayList<>(); // 오려 두기 판         for (int t = 0; t < q; t++) {            String[] p = in.readLine().trim().split(" ");            if (p[0].equals("copy")) {                int l = Integer.parseInt(p[1]), r = Integer.parseInt(p[2]);                // subList는 원래 목록을 들여다보는 창이라서, 새 목록에 담아 사본으로 만든다                clip = new ArrayList<>(a.subList(l - 1, r));            } else if (p[0].equals("cut")) {                int l = Integer.parseInt(p[1]), r = Integer.parseInt(p[2]);                clip = new ArrayList<>(a.subList(l - 1, r));                a.subList(l - 1, r).clear(); // 창을 비우면 원래 목록에서 빠진다            } else {                int at = Integer.parseInt(p[1]);                if (a.size() + clip.size() <= limit) a.addAll(at, clip);            }        }         StringBuilder out = new StringBuilder();        out.append(a.size()).append('\n');        if (a.isEmpty()) out.append('-');        for (int i = 0; i < a.size(); i++) {            if (i > 0) out.append(' ');            out.append(a.get(i));        }        System.out.println(out);    }}
C#Main.cs · 38 行
using System;using System.Collections.Generic;using System.Linq; class Program{    static void Main()    {        int limit = int.Parse(Console.ReadLine().Trim().Split(' ')[1]);        List<int> a = Console.ReadLine().Trim().Split(' ').Select(int.Parse).ToList();        int q = int.Parse(Console.ReadLine().Trim());        var clip = new List<int>(); // 오려 두기 판         for (int t = 0; t < q; t++)        {            string[] p = Console.ReadLine().Trim().Split(' ');            if (p[0] == "copy")            {                int l = int.Parse(p[1]), r = int.Parse(p[2]);                clip = a.GetRange(l - 1, r - l + 1); // GetRange(시작, 개수): 사본을 준다            }            else if (p[0] == "cut")            {                int l = int.Parse(p[1]), r = int.Parse(p[2]);                clip = a.GetRange(l - 1, r - l + 1);                a.RemoveRange(l - 1, r - l + 1);            }            else            {                int at = int.Parse(p[1]);                if (a.Count + clip.Count <= limit) a.InsertRange(at, clip);            }        }         Console.WriteLine(a.Count);        Console.WriteLine(a.Count > 0 ? string.Join(" ", a) : "-");    }}
C++main.cpp · 34 行
#include <iostream>#include <string>#include <vector> int main() {    std::ios::sync_with_stdio(false);    std::size_t n, limit;    std::cin >> n >> limit;    std::vector<int> a(n), clip; // clip: 오려 두기 판    for (int& x : a) std::cin >> x;    int q;    std::cin >> q;     for (int t = 0; t < q; t++) {        std::string cmd;        std::cin >> cmd;        if (cmd == "paste") {            int at;            std::cin >> at;            if (a.size() + clip.size() <= limit) a.insert(a.begin() + at, clip.begin(), clip.end());        } else {            int l, r;            std::cin >> l >> r;            // 반복자 두 개 [시작, 끝)로 조각을 가리킨다. assign은 사본을 만든다            clip.assign(a.begin() + (l - 1), a.begin() + r);            if (cmd == "cut") a.erase(a.begin() + (l - 1), a.begin() + r);        }    }     std::cout << a.size() << '\n';    if (a.empty()) std::cout << '-';    for (std::size_t i = 0; i < a.size(); i++) std::cout << (i ? " " : "") << a[i];    std::cout << '\n';}
Cmain.c · 43 行
#include <stdio.h>#include <string.h> #define MAX 5000 static int a[MAX], clip[MAX]; int main(void) {    int n, limit, q, clipLen = 0;    scanf("%d %d", &n, &limit);    for (int i = 0; i < n; i++) scanf("%d", &a[i]);    scanf("%d", &q);     for (int t = 0; t < q; t++) {        char cmd[8];        scanf("%7s", cmd);        if (strcmp(cmd, "paste") == 0) {            int at;            scanf("%d", &at);            if (n + clipLen <= limit) {                /* 뒤쪽을 조각 길이만큼 밀고(겹치므로 memmove) 빈 곳에 조각을 베낀다 */                memmove(a + at + clipLen, a + at, (size_t)(n - at) * sizeof(int));                memcpy(a + at, clip, (size_t)clipLen * sizeof(int));                n += clipLen;            }        } else {            int l, r;            scanf("%d %d", &l, &r);            clipLen = r - l + 1;            memcpy(clip, a + l - 1, (size_t)clipLen * sizeof(int)); /* 조각을 따로 베껴 둔다 */            if (strcmp(cmd, "cut") == 0) {                memmove(a + l - 1, a + r, (size_t)(n - r) * sizeof(int));                n -= clipLen;            }        }    }     printf("%d\n", n);    if (n == 0) printf("-");    for (int i = 0; i < n; i++) printf(i ? " %d" : "%d", a[i]);    printf("\n");    return 0;}
Craykonmain.cray · 54 行
첫줄 = コンソール.数読み();limit = 첫줄[1];a = コンソール.数読み();q = コンソール.行読み().数に();clip = []; // 목록 뒤에 다른 목록의 것들을 차례로 붙인다붙이기(목록, 더할것){    それぞれ (x の中 더할것)    {        목록.追加(x);    }} 繰り返す (t = 0; t < q; t++){    p = コンソール.行読み().空白除去().分ける(" ");    もし (p[0] == "paste")    {        at = p[1].数に();        もし (a.個数 + clip.個数 <= limit)        {            // 앞 조각 + 오려 둔 것 + 뒤 조각            뒤 = a.部分リスト(at, a.個数 - at);            a = a.部分リスト(0, at);            붙이기(a, clip);            붙이기(a, 뒤);        }    }    でなければ    {        l = p[1].数に();        r = p[2].数に();        // 부분목록(시작, 개수)는 새 목록(사본)을 준다        clip = a.部分リスト(l - 1, r - l + 1);        もし (p[0] == "cut")        {            뒤 = a.部分リスト(r, a.個数 - r);            a = a.部分リスト(0, l - 1);            붙이기(a, 뒤);        }    }} コンソール.書く(a.個数);もし (a.個数 > 0){    コンソール.書く(a.つなぐ(" "));}でなければ{    コンソール.書く("-");}