{ }codelper
改行なし出力シミュレーションP0271

進捗バーが残した文字

あるプログラムがターミナル画面に文字を書いた出力命令の記録が与えられる。
画面は最初、空の行が一つだけで、カーソルはその行の 1 番のマスにある。
行のマスは左から 1 番、2 番、3 番、… と数える。
命令は次の六種類である。
write s は改行せずに文字列 s の文字を左から一つずつ書く。
一文字書くとき、カーソルのマスにすでに文字があればその文字を新しい文字で上書きし、カーソルが行の最後の文字のすぐ後ろにあれば行の末尾に付け足す。
そのあとカーソルが右に一マス進む。
どの命令もカーソルを行の最後の文字のすぐ後ろより右へは動かさないので、文字を書くときは常にこの二つの場合のどちらかである。
print s は write s をしてから nl をする。
nl は今の行の下に空の行を一つ新しく作り、カーソルを新しい行の 1 番のマスに移す。
cr はカーソルを今の行の 1 番のマスに移す。
文字は消さない。
back k はカーソルを左に k マス移す。
カーソルは 1 番のマスより左には行かず、1 番のマスで止まる。
文字は消さない。
erase は今の行で、カーソルのマスとその右にある文字をすべて消す。
カーソルは動かない。
カーソルは常にいちばん下の行にある。
一行目に命令の数 q、続く q 行に命令が一つずつ与えられる。
命令名と s、k の間は空白一つである。
すべての命令を処理したあと、画面の行を上から順に一行ずつ出力せよ。
文字のない行は空行として出力する。
ただし、いちばん下の行が空ならその行は出力しない。

入力
1 ≤ q ≤ 2,000。
s は空白でない ASCII 文字(コード 33~126)からなる 1~30 文字の文字列。
1 ≤ k ≤ 1,000。
どの行も 1,000 文字を超えない。
画面には文字が一つ以上残る。
出力
画面の行
例 1 · stdin
7
write loading...
cr
write 50%
cr
write done
nl
print bye
stdout
doneing...
bye
例 2 · stdin
12
write abcdef
back 2
write XY
write Z
back 100
write 1
nl
nl
write hello
back 3
erase
write p!
stdout
1bcdXYZ

hep!
main.py
入力(stdin)

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

$ 準備完了

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

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

改行せずに書く方法:Python print(s, end="")・sys.stdout.write(s)、JS・TS process.stdout.write(s)(console.log は必ず改行します)、Java System.out.print(改行するのは println)、C# Console.Write(改行するのは WriteLine)、C fputs(s, stdout)・printf("%s", s)、C++ std::cout << s(std::endl か '\n' を付けたときだけ改行)、Craykon コンソール.続けて書く(s)(コンソール.書く は改行します)。
この問題の cr は実際のターミナルの \r です。
進捗を一行で書き直そうと print(f"\r{p}%", end="") のように書きますが、落とし穴 ① \r はカーソルを動かすだけで行を消しません。
短い文字で上書きすると長い文字の尻尾が残り、loading... の上に done を書くと doneing... が見えます。
実際には空白で埋めるか、erase にあたる \x1b[K を送ります。
② back は 1 番のマスで止まります。
カーソルを 0 始まりの数で持ってそのまま引くと、Python では cur[-1] が末尾のマスなのでエラーなしに違う文字を上書きし、C・C++ は配列の外に書きます。
C++ でカーソルを size_t にすると負の数がなく巨大な数になるので、引く前に比べます。
③ erase はカーソルのマスから右だけを消します:Python del cur[col:]、JS・TS cur.length = col、Java setLength(col)、C# Length = col、C++ resize(col)、Craykon 部分リスト(0, col)。
④ 上書きは挿入ではありません。
C++ や Python の insert を使うと後ろの文字がずれます。
⑤ 名前がまぎらわしい:この問題の print は改行しますが、Java の print・C# の Write は改行しません。
⑥ C でためた行を printf(cur) で出すと % を書式として読むので 50%d… が壊れます。
fputs か printf("%s", cur) を使います。

Pythonmain.py · 46 行
import sys lines = sys.stdin.read().split("\n")q = int(lines[0])screen = []  # 다 쓴 줄들cur = []     # 지금 줄의 글자들col = 0      # 커서 칸(0부터)  def write(s):    global col    for ch in s:        if col < len(cur):            cur[col] = ch  # 글자가 있으면 덮어쓴다        else:            cur.append(ch)  # 줄 끝 바로 뒤면 붙인다        col += 1  def newline():    global cur, col    screen.append("".join(cur))    cur = []    col = 0  for line in lines[1:1 + q]:    p = line.split()    cmd = p[0]    if cmd == "write":        write(p[1])    elif cmd == "print":        write(p[1])        newline()    elif cmd == "nl":        newline()    elif cmd == "cr":        col = 0  # 글자는 그대로 두고 커서만 맨 앞으로    elif cmd == "back":        col = max(0, col - int(p[1]))  # 음수 자리는 맨 끝 칸이 되니 0에서 멈춘다    else:  # erase        del cur[col:] if cur:    screen.append("".join(cur))sys.stdout.write("".join(row + "\n" for row in screen))
JavaScriptmain.js · 33 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const q = Number(lines[0]);const screen = []; // 다 쓴 줄들let cur = []; // 지금 줄의 글자들let col = 0; // 커서 칸(0부터) function write(s) {  for (const ch of s) {    cur[col] = ch; // 글자가 있으면 덮어쓰고, 줄 끝 바로 뒤면 붙인다    col++;  }} function newline() {  screen.push(cur.join(""));  cur = [];  col = 0;} for (const line of lines.slice(1, 1 + q)) {  const [cmd, arg] = line.trim().split(" ");  if (cmd === "write") write(arg);  else if (cmd === "print") {    write(arg);    newline();  } else if (cmd === "nl") newline();  else if (cmd === "cr") col = 0; // 글자는 그대로, 커서만 맨 앞으로  else if (cmd === "back") col = Math.max(0, col - Number(arg));  else cur.length = col; // erase: 커서 칸부터 뒤를 잘라 낸다}if (cur.length > 0) screen.push(cur.join(""));// console.log는 늘 줄을 바꾼다. 줄 바꿈 없이 쓰려면 process.stdout.writeprocess.stdout.write(screen.map((row) => row + "\n").join(""));
TypeScriptmain.ts · 35 行
import { readFileSync } from "node:fs"; const lines: string[] = readFileSync(0, "utf8").split("\n");const q = Number(lines[0]);const screen: string[] = []; // 다 쓴 줄들let cur: string[] = []; // 지금 줄의 글자들let col = 0; // 커서 칸(0부터) function write(s: string): void {  for (const ch of s) {    cur[col] = ch; // 글자가 있으면 덮어쓰고, 줄 끝 바로 뒤면 붙인다    col++;  }} function newline(): void {  screen.push(cur.join(""));  cur = [];  col = 0;} for (const line of lines.slice(1, 1 + q)) {  const [cmd, arg] = line.trim().split(" ");  if (cmd === "write") write(arg);  else if (cmd === "print") {    write(arg);    newline();  } else if (cmd === "nl") newline();  else if (cmd === "cr") col = 0; // 글자는 그대로, 커서만 맨 앞으로  else if (cmd === "back") col = Math.max(0, col - Number(arg));  else cur.length = col; // erase: 커서 칸부터 뒤를 잘라 낸다}if (cur.length > 0) screen.push(cur.join(""));// console.log는 늘 줄을 바꾼다. 줄 바꿈 없이 쓰려면 process.stdout.writeprocess.stdout.write(screen.map((row) => row + "\n").join(""));
JavaMain.java · 40 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.PrintWriter; public class Main {    static StringBuilder cur = new StringBuilder(); // 지금 줄    static int col = 0; // 커서 칸(0부터)     static void write(String s) {        for (char ch : s.toCharArray()) {            if (col < cur.length()) cur.setCharAt(col, ch); // 덮어쓴다            else cur.append(ch); // 줄 끝 바로 뒤면 붙인다            col++;        }    }     public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        PrintWriter out = new PrintWriter(System.out);        int q = Integer.parseInt(in.readLine().trim());        for (int t = 0; t < q; t++) {            String[] p = in.readLine().trim().split(" ");            switch (p[0]) {                case "write" -> write(p[1]);                case "print", "nl" -> {                    if (p[0].equals("print")) write(p[1]);                    out.println(cur); // 다 쓴 줄은 바로 내보낸다                    cur.setLength(0);                    col = 0;                }                case "cr" -> col = 0; // 글자는 그대로, 커서만 맨 앞으로                case "back" -> col = Math.max(0, col - Integer.parseInt(p[1]));                default -> cur.setLength(col); // erase            }        }        if (cur.length() > 0) out.println(cur);        out.flush();    }}
C#Main.cs · 58 行
using System;using System.IO;using System.Text; class Program{    static StringBuilder cur = new StringBuilder(); // 지금 줄    static int col = 0; // 커서 칸(0부터)     static void Write(string s)    {        foreach (char ch in s)        {            if (col < cur.Length) cur[col] = ch; // 덮어쓴다            else cur.Append(ch); // 줄 끝 바로 뒤면 붙인다            col++;        }    }     static void Main()    {        var output = new StreamWriter(Console.OpenStandardOutput());        int q = int.Parse(Console.ReadLine().Trim());        for (int t = 0; t < q; t++)        {            string[] p = Console.ReadLine().Trim().Split(' ');            switch (p[0])            {                case "write":                    Write(p[1]);                    break;                case "print":                case "nl":                    if (p[0] == "print") Write(p[1]);                    output.Write(cur.ToString());                    output.Write('\n');                    cur.Clear();                    col = 0;                    break;                case "cr":                    col = 0; // 글자는 그대로, 커서만 맨 앞으로                    break;                case "back":                    col = Math.Max(0, col - int.Parse(p[1]));                    break;                default: // erase                    cur.Length = col;                    break;            }        }        if (cur.Length > 0)        {            output.Write(cur.ToString());            output.Write('\n');        }        output.Flush();    }}
C++main.cpp · 41 行
#include <algorithm>#include <iostream>#include <string> int main() {    std::ios::sync_with_stdio(false);    int q;    std::cin >> q;    std::string cur; // 지금 줄    std::size_t col = 0; // 커서 칸(0부터)    std::string out;    for (int t = 0; t < q; t++) {        std::string cmd, arg;        std::cin >> cmd;        if (cmd == "write" || cmd == "print") {            std::cin >> arg;            for (char ch : arg) {                if (col < cur.size()) cur[col] = ch; // 덮어쓴다                else cur += ch; // 줄 끝 바로 뒤면 붙인다                col++;            }        }        if (cmd == "print" || cmd == "nl") {            out += cur;            out += '\n';            cur.clear();            col = 0;        } else if (cmd == "cr") {            col = 0; // 글자는 그대로, 커서만 맨 앞으로        } else if (cmd == "back") {            std::size_t k;            std::cin >> k;            col = col > k ? col - k : 0; // size_t는 음수가 없어서 빼기 전에 비교한다        } else if (cmd == "erase") {            cur.resize(col);        }    }    if (!cur.empty()) out += cur + '\n';    std::cout << out;    return 0;}
Cmain.c · 45 行
#include <stdio.h>#include <string.h> static char cur[1001]; /* 지금 줄(한 줄은 1,000칸 이하) */static int len = 0, col = 0; /* 줄 길이, 커서 칸(0부터) */ static void write_text(const char *s) {    for (; *s; s++) {        cur[col++] = *s; /* 덮어쓰거나, 줄 끝 바로 뒤면 붙인다 */        if (col > len) len = col;    }} static void newline(void) {    cur[len] = '\0';    fputs(cur, stdout); /* printf(cur)는 안 된다: %를 서식으로 읽는다 */    putchar('\n');    len = col = 0;} int main(void) {    int q;    char cmd[8], arg[32];    scanf("%d", &q);    for (int t = 0; t < q; t++) {        scanf("%7s", cmd);        if (strcmp(cmd, "write") == 0 || strcmp(cmd, "print") == 0) {            scanf("%31s", arg);            write_text(arg);            if (cmd[0] == 'p') newline();        } else if (strcmp(cmd, "nl") == 0) {            newline();        } else if (strcmp(cmd, "cr") == 0) {            col = 0;        } else if (strcmp(cmd, "back") == 0) {            int k;            scanf("%d", &k);            col = col > k ? col - k : 0;        } else { /* erase */            len = col;        }    }    if (len > 0) newline();    return 0;}
Craykonmain.cray · 57 行
q = コンソール.行読み().数に();cur = []; // 지금 줄의 글자들col = 0; // 커서 칸(0부터) 쓰기(s){    それぞれ (ch の中 s)    {        もし (col < cur.個数)        {            cur[col] = ch; // 글자가 있으면 덮어쓴다        }        でなければ        {            cur.追加(ch); // 줄 끝 바로 뒤면 붙인다        }        col = col + 1;    }} 줄바꾸기(){    コンソール.書く(cur.つなぐ(""));    cur = [];    col = 0;} 繰り返す (t = 0; t < q; t++){    p = コンソール.行読み().空白除去().分ける(" ");    選択 (p[0])    {        場合 "write":            쓰기(p[1]);            抜ける;        場合 "print":            쓰기(p[1]);            줄바꾸기();            抜ける;        場合 "nl":            줄바꾸기();            抜ける;        場合 "cr":            col = 0; // 글자는 그대로, 커서만 맨 앞으로            抜ける;        場合 "back":            col = 数学.大きい方(0, col - p[1].数に());            抜ける;        それ以外:            cur = cur.部分リスト(0, col); // erase: 커서 칸부터 뒤를 버린다            抜ける;    }}もし (cur.個数 > 0){    コンソール.書く(cur.つなぐ(""));}