n 個の文字が一列に書かれたテープ s がある。 q 個の命令を与えられた順に処理する。 L k はテープの先頭の文字を外して末尾につなぐことを k 回行う。 R k はテープの末尾の文字を外して先頭につなぐことを k 回行う。 F はテープ全体を逆順にする(末尾の文字が先頭に来る)。 W p c は今のテープの前から p 番目の文字を文字 c に書き換える。 Q p は今のテープの前から p 番目の文字を一行に出力する。 番号 p は先頭の文字を 1 として数える。 k は n より大きいことがある。 一行目に s、二行目に q、続く q 行に命令が一つずつ与えられる。 命令の中の文字と数は空白一つで区切られる。 Q 命令ごとに文字を一つ一行に出力し、最後の行にはすべての命令を終えたあとのテープ全体を出力せよ。
入力
s は英小文字と数字からなる文字列で、長さ n は 1 ≤ n ≤ 10,000。 1 ≤ q ≤ 10,000。 L と R の k は 1 ≤ k ≤ 1,000,000,000(カンマなしの数字だけで与えられる)。 W と Q の p は 1 ≤ p ≤ n。 W の c は英小文字か数字一文字。 Q 命令がないこともある
出力
Q 命令ごとに一文字ずつ一行、最後の行に長さ n のテープ
例 1 · stdin
abcdef
10
Q 1
L 2
Q 1
F
Q 1
L 1
Q 1
W 2 x
R 9
Q 6
stdout
a
c
b
a
e
dcbaxe
main.py
エラー一覧00F8 ↓ · ⇧F8 ↑
入力(stdin)
実行すると、これらの行が順に input()・標準入力に渡されます(実行中に入力を求めません)。
$ 準備完了
コードを入力して解くにはパソコンで開いてください。モバイルでは問題と解答を見られます。
ほかの言語では?言語別の解答を見る · 8 言語の解答
添字で読む書き方は Python・JS・TS・C#・C・C++・Craykon が s[i]、Java が s.charAt(i) です。 その位置に書くのは言語ごとに違います。 Python の s[i] = c は TypeError、Java にはその構文がなく、C# と TS はコンパイルエラー(読み取り専用)、Craykon は「文字列は一文字ずつ変えられません」で止まります。 いちばん危ないのは JS です:s[i] = c がエラーなしで無視され(strict モードのときだけ TypeError)、テープはそのままです。 そこで書き換えられる文字のリストに変えます:Python list(s)、JS・TS [...s]、Java toCharArray()、C# ToCharArray()、Craykon は文字を一つずつリストに 追加。 C の char 配列と C++ の std::string は s[i] = c がそのまま使えます。 命令のたびにテープを実際に回したり裏返したりせず、「今のテープの i 番の文字は元の文字列の (開始 + 向き × i) 番」という二つの数だけを直せば、命令一つが計算一回で済みます。 落とし穴 ① その番号は負になることがあります。 Python の % はいつも 0 ~ n − 1 を返しますが、C・C++・Java・C#・JS・TS・Craykon では -2 % 5 は -2 です。 JS・TS は tape[-2] がエラーなしで undefined になるので、その文字が抜けるか(join)"undefined" と出力され(+ でつなぐとき)、Java・C#・Craykon はエラーで止まり、C・C++ は配列の外を読みます。 ((x % n) + n) % n か Java の Math.floorMod(x, n) を使います。 ② 裏返したあとは「先頭の文字を後ろへ送る」が元の文字列では逆向きの動きになるので、L と R の k に向きを掛けます。 ③ 裏返すときは向きを変えるだけでなく、開始位置も今の末尾の文字へ移します。 ④ 人が数えた p 番目は 0 から数えると p - 1 番です。 ⑤ k は 10 億まであるので一つずつ k 回回すことはできず、減らさないまま int に足していくと三回目で 21 億を超えて値が回ります。 先に k % n で減らします。
Pythonmain.py · 31 行
import syslines = sys.stdin.read().split("\n")tape = list(lines[0].strip()) # 문자열은 한 글자씩 고칠 수 없어서 글자 목록으로 바꾼다n = len(tape)q = int(lines[1])# 띠를 실제로 돌리지 않는다. 지금 띠의 i번 글자(0부터)는 tape[(start + step * i) % n]start, step = 0, 1def where(i): return (start + step * i) % n # Python의 %는 음수에서도 0 ~ n-1을 준다out = []for line in lines[2:2 + q]: p = line.split() if p[0] == "L": start = where(int(p[1]) % n) elif p[0] == "R": start = where(-(int(p[1]) % n)) elif p[0] == "F": start = where(n - 1) # 맨 끝 글자가 새 맨 앞 step = -step elif p[0] == "W": tape[where(int(p[1]) - 1)] = p[2] else: out.append(tape[where(int(p[1]) - 1)])out.append("".join(tape[where(i)] for i in range(n)))print("\n".join(out))
JavaScriptmain.js · 28 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const tape = [...lines[0].trim()]; // 문자열은 한 글자씩 고칠 수 없어서 글자 배열로 바꾼다const n = tape.length;const q = Number(lines[1]);// 띠를 실제로 돌리지 않는다. 지금 띠의 i번 글자(0부터)는 tape[(start + step * i) % n]let start = 0, step = 1;// JS의 %는 음수에서 음수를 주므로 n을 더해 한 번 더 나눈다const where = (i) => (((start + step * i) % n) + n) % n;const out = [];for (const line of lines.slice(2, 2 + q)) { const p = line.trim().split(" "); if (p[0] === "L") { start = where(Number(p[1]) % n); } else if (p[0] === "R") { start = where(-(Number(p[1]) % n)); } else if (p[0] === "F") { start = where(n - 1); // 맨 끝 글자가 새 맨 앞 step = -step; } else if (p[0] === "W") { tape[where(Number(p[1]) - 1)] = p[2]; } else { out.push(tape[where(Number(p[1]) - 1)]); }}out.push(tape.map((_, i) => tape[where(i)]).join(""));console.log(out.join("\n"));
TypeScriptmain.ts · 30 行
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const tape: string[] = [...lines[0].trim()]; // 문자열은 한 글자씩 고칠 수 없어서 글자 배열로 바꾼다const n = tape.length;const q = Number(lines[1]);// 띠를 실제로 돌리지 않는다. 지금 띠의 i번 글자(0부터)는 tape[(start + step * i) % n]let start = 0, step = 1;// TS의 %는 음수에서 음수를 주므로 n을 더해 한 번 더 나눈다const where = (i: number): number => (((start + step * i) % n) + n) % n;const out: string[] = [];for (const line of lines.slice(2, 2 + q)) { const p = line.trim().split(" "); if (p[0] === "L") { start = where(Number(p[1]) % n); } else if (p[0] === "R") { start = where(-(Number(p[1]) % n)); } else if (p[0] === "F") { start = where(n - 1); // 맨 끝 글자가 새 맨 앞 step = -step; } else if (p[0] === "W") { tape[where(Number(p[1]) - 1)] = p[2]; } else { out.push(tape[where(Number(p[1]) - 1)]); }}out.push(tape.map((_, i) => tape[where(i)]).join(""));console.log(out.join("\n"));
JavaMain.java · 35 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;public class Main { static int n, start = 0, step = 1; // 띠를 실제로 돌리지 않는다. 지금 띠의 i번 글자(0부터)가 있는 원래 자리 static int where(int i) { return Math.floorMod(start + step * i, n); // %와 달리 음수에서도 0 ~ n-1을 준다 } public static void main(String[] args) throws IOException { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); char[] tape = in.readLine().trim().toCharArray(); // String은 고칠 수 없어서 char[]로 바꾼다 n = tape.length; int q = Integer.parseInt(in.readLine().trim()); StringBuilder out = new StringBuilder(); for (int t = 0; t < q; t++) { String[] p = in.readLine().trim().split(" "); switch (p[0]) { case "L" -> start = where(Integer.parseInt(p[1]) % n); case "R" -> start = where(-(Integer.parseInt(p[1]) % n)); case "F" -> { start = where(n - 1); // 맨 끝 글자가 새 맨 앞 step = -step; } case "W" -> tape[where(Integer.parseInt(p[1]) - 1)] = p[2].charAt(0); default -> out.append(tape[where(Integer.parseInt(p[1]) - 1)]).append('\n'); } } for (int i = 0; i < n; i++) out.append(tape[where(i)]); System.out.println(out); }}
C#Main.cs · 46 行
using System;using System.Text;class Program{ static int n, start = 0, step = 1; // 띠를 실제로 돌리지 않는다. 지금 띠의 i번 글자(0부터)가 있는 원래 자리 static int Where(int i) { return ((start + step * i) % n + n) % n; // C#의 %는 음수에서 음수를 준다 } static void Main() { char[] tape = Console.ReadLine().Trim().ToCharArray(); // string은 고칠 수 없어서 char[]로 바꾼다 n = tape.Length; int q = int.Parse(Console.ReadLine().Trim()); var output = new StringBuilder(); for (int t = 0; t < q; t++) { string[] p = Console.ReadLine().Trim().Split(' '); switch (p[0]) { case "L": start = Where(int.Parse(p[1]) % n); break; case "R": start = Where(-(int.Parse(p[1]) % n)); break; case "F": start = Where(n - 1); // 맨 끝 글자가 새 맨 앞 step = -step; break; case "W": tape[Where(int.Parse(p[1]) - 1)] = p[2][0]; break; default: output.Append(tape[Where(int.Parse(p[1]) - 1)]).Append('\n'); break; } } for (int i = 0; i < n; i++) output.Append(tape[Where(i)]); Console.WriteLine(output); }}
C++main.cpp · 41 行
#include <iostream>#include <string>int main() { std::ios::sync_with_stdio(false); std::string tape; // std::string은 tape[i] = c로 한 글자씩 고칠 수 있다 int q; std::cin >> tape >> q; const int n = static_cast<int>(tape.size()); // 띠를 실제로 돌리지 않는다. 지금 띠의 i번 글자(0부터)는 tape[(start + step * i) % n] int start = 0, step = 1; // C++의 %는 음수에서 음수를 주므로 n을 더해 한 번 더 나눈다 auto where = [&](int i) { return ((start + step * i) % n + n) % n; }; std::string out; for (int t = 0; t < q; t++) { char cmd, c; int v; std::cin >> cmd; if (cmd == 'L') { std::cin >> v; start = where(v % n); } else if (cmd == 'R') { std::cin >> v; start = where(-(v % n)); } else if (cmd == 'F') { start = where(n - 1); // 맨 끝 글자가 새 맨 앞 step = -step; } else if (cmd == 'W') { std::cin >> v >> c; tape[where(v - 1)] = c; } else { std::cin >> v; out += tape[where(v - 1)]; out += '\n'; } } for (int i = 0; i < n; i++) out += tape[where(i)]; std::cout << out << '\n';}
Cmain.c · 45 行
#include <stdio.h>#include <string.h>static char tape[10001]; /* char 배열은 한 글자씩 고칠 수 있다 */static int n, start = 0, step = 1;/* 띠를 실제로 돌리지 않는다. 지금 띠의 i번 글자(0부터)가 있는 원래 자리 */static int where(int i) { return ((start + step * i) % n + n) % n; /* C의 %는 음수에서 음수를 준다 */}int main(void) { int q; scanf("%10000s %d", tape, &q); n = (int)strlen(tape); for (int t = 0; t < q; t++) { char cmd, c; int v; scanf(" %c", &cmd); switch (cmd) { case 'L': scanf("%d", &v); start = where(v % n); break; case 'R': scanf("%d", &v); start = where(-(v % n)); break; case 'F': start = where(n - 1); /* 맨 끝 글자가 새 맨 앞 */ step = -step; break; case 'W': scanf("%d %c", &v, &c); tape[where(v - 1)] = c; break; default: scanf("%d", &v); printf("%c\n", tape[where(v - 1)]); } } for (int i = 0; i < n; i++) putchar(tape[where(i)]); putchar('\n'); return 0;}
Craykonmain.cray · 50 行
줄 = コンソール.行読み().空白除去();// 문자열은 한 글자씩 고칠 수 없어서 글자 목록으로 바꾼다tape = [];それぞれ (c の中 줄){ tape.追加(c);}n = tape.個数;q = コンソール.行読み().数に();// 띠를 실제로 돌리지 않는다. 지금 띠의 i번 글자(0부터)는 tape[(start + step * i) % n]start = 0;step = 1;// 크레이콘의 %는 음수에서 음수를 주므로 n을 더해 한 번 더 나눈다자리(i){ 返す ((start + step * i) % n + n) % n;}繰り返す (t = 0; t < q; t++){ p = コンソール.行読み().空白除去().分ける(" "); 選択 (p[0]) { 場合 "L": start = 자리(p[1].数に() % n); 抜ける; 場合 "R": start = 자리(-(p[1].数に() % n)); 抜ける; 場合 "F": // 맨 끝 글자가 새 맨 앞 start = 자리(n - 1); step = -step; 抜ける; 場合 "W": tape[자리(p[1].数に() - 1)] = p[2]; 抜ける; それ以外: コンソール.書く(tape[자리(p[1].数に() - 1)]); 抜ける; }}끝 = [];繰り返す (i = 0; i < n; i++){ 끝.追加(tape[자리(i)]);}コンソール.書く(끝.つなぐ(""));