{ }codelper
字符串下标负数取余P0297

又转又翻的字符带

有一条写着 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
输入(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 不报错而被忽略(只有严格模式才是 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 sys lines = 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, 1  def 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)]);}控制台.输出(끝.连接(""));