{ }codelper
飛ばしながらループ文字列の組み立て文字列の長さP0172

文字で描いた定規

文字で定規を描いて出力する。
一行目に定規の長さ n と大きい目盛りの間隔 k が空白一つで区切って与えられる。
定規には 0 から n までの n + 1 個のマスがあり、マスの番号は左から 0, 1, 2, …, n である。
一行目は目盛りの行である。
ちょうど n + 1 文字で、マス c の文字は c が k の倍数なら |、そうでなければ .
である。
二行目は数の行である。
0, k, 2k, 3k, … のように n 以下の k の倍数 m を小さい順に見て、m を十進数でマス m から右へ書こうとする。
m を書くと占める最後のマスは m + (m の桁数)− 1 である。
このマスが n より大きければ m は書かない。
また、すでに書いた数があれば、その数の最後のマスとマス m の間に空白が少なくとも一つなければならない。
そうでなければ m は書かない。
書かずに飛ばした数は、そのあとの判断に何の影響も与えない。
数を書かなかったマスは空白で、数の行の末尾の空白は出力しない。
三行目には数の行に書いた数の個数を出力する。

入力
1 ≤ k ≤ n ≤ 5000
出力
三行
例 1 · stdin
25 5
stdout
|....|....|....|....|....|
0    5    10   15   20
5
例 2 · stdin
12 1
stdout
|||||||||||||
0 2 4 6 8 10
6
main.py
入力(stdin)

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

$ 準備完了

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

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

目盛りは k ずつ飛ぶループです。
Python の range(0, n + 1, k) は終わりを含まないので、n + 1 まで渡さないとマス n が入りません。
ほかの言語は for (m = 0; m <= n; m += k)、Craykon は 繰り返す (m = 0; m <= n; m += k) です。
目盛りの行を c < n で回すと n 文字しか出ず、最初の例の最後の | が抜けます。
数の行は左から順につなげていけば、いまの長さが次の空きマスの番号なので、「空白が一つ以上」は m > いまの長さ、足りない空白は m - いまの長さ 個です。
飛ばした数の最後のマスを「前の数」として覚えると、二つ目の例で 2, 4, 6 も抜けます。
収まるかは <= なので、< にすると n = 9, k = 9 の 9 が消えます。
桁数は数を文字列にして長さを測ります。
Python len(str(m))、JS・TS String(m).length、Java Integer.toString(m).length()、C# m.ToString().Length、C++ std::to_string(m).size()、Craykon ("" + m).長さ です。
C は sprintf が返す書いた文字数を使えます。
文字列のつなぎ方も違います。
Java・C# はループで += すると毎回新しい文字列をコピーするので StringBuilder を使います(C# は Append(' ', 個数) で空白を一度に足せます)。
Python・JS・TS・C++ の std::string・Craykon は += で十分で、C は文字配列に詰めて最後に '\0' を入れます。
C++ の size() は符号なしなので、m と比べる前に int に変えておくのが安全です。

Pythonmain.py · 19 行
n, k = map(int, input().split()) # 눈금 줄: 칸 0 ~ n, 모두 n + 1글자marks = "".join("|" if c % k == 0 else "." for c in range(n + 1)) # 숫자 줄: 0, k, 2k, ... 를 차례로 보며 쓸 수 있는 것만 뒤에 붙인다labels = ""count = 0for m in range(0, n + 1, k):    t = str(m)    fits = m + len(t) - 1 <= n             # 마지막 칸이 n을 넘지 않는다    gap = labels == "" or m > len(labels)  # 앞 숫자와 빈칸이 하나 이상 있다    if fits and gap:        labels += " " * (m - len(labels)) + t        count += 1 print(marks)print(labels)print(count)
JavaScriptmain.js · 19 行
const [n, k] = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number); // 눈금 줄: 칸 0 ~ n, 모두 n + 1글자let marks = "";for (let c = 0; c <= n; c++) marks += c % k === 0 ? "|" : "."; // 숫자 줄: 0, k, 2k, ... 를 차례로 보며 쓸 수 있는 것만 뒤에 붙인다let labels = "";let count = 0;for (let m = 0; m <= n; m += k) {  const t = String(m);  const fits = m + t.length - 1 <= n; // 마지막 칸이 n을 넘지 않는다  const gap = labels === "" || m > labels.length; // 앞 숫자와 빈칸이 하나 이상 있다  if (fits && gap) {    labels += " ".repeat(m - labels.length) + t;    count++;  }}console.log([marks, labels, count].join("\n"));
TypeScriptmain.ts · 20 行
import { readFileSync } from "node:fs";const [n, k]: number[] = readFileSync(0, "utf8").trim().split(/\s+/).map(Number); // 눈금 줄: 칸 0 ~ n, 모두 n + 1글자let marks = "";for (let c = 0; c <= n; c++) marks += c % k === 0 ? "|" : "."; // 숫자 줄: 0, k, 2k, ... 를 차례로 보며 쓸 수 있는 것만 뒤에 붙인다let labels = "";let count = 0;for (let m = 0; m <= n; m += k) {  const t: string = String(m);  const fits: boolean = m + t.length - 1 <= n; // 마지막 칸이 n을 넘지 않는다  const gap: boolean = labels === "" || m > labels.length; // 앞 숫자와 빈칸이 하나 이상 있다  if (fits && gap) {    labels += " ".repeat(m - labels.length) + t;    count++;  }}console.log([marks, labels, String(count)].join("\n"));
JavaMain.java · 28 行
import java.util.Scanner; public class Main {    public static void main(String[] args) {        Scanner sc = new Scanner(System.in);        int n = sc.nextInt(), k = sc.nextInt();         // 눈금 줄: 칸 0 ~ n, 모두 n + 1글자        StringBuilder marks = new StringBuilder();        for (int c = 0; c <= n; c++) marks.append(c % k == 0 ? '|' : '.');         // 숫자 줄: 0, k, 2k, ... 를 차례로 보며 쓸 수 있는 것만 뒤에 붙인다        StringBuilder labels = new StringBuilder();        int count = 0;        for (int m = 0; m <= n; m += k) {            String t = Integer.toString(m);            boolean fits = m + t.length() - 1 <= n; // 마지막 칸이 n을 넘지 않는다            boolean gap = labels.length() == 0 || m > labels.length(); // 앞 숫자와 빈칸이 하나 이상            if (fits && gap) {                labels.append(" ".repeat(m - labels.length())).append(t);                count++;            }        }        System.out.println(marks);        System.out.println(labels);        System.out.println(count);    }}
C#Main.cs · 33 行
using System;using System.Text; class Program{    static void Main()    {        string[] p = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);        int n = int.Parse(p[0]), k = int.Parse(p[1]);         // 눈금 줄: 칸 0 ~ n, 모두 n + 1글자        var marks = new StringBuilder();        for (int c = 0; c <= n; c++) marks.Append(c % k == 0 ? '|' : '.');         // 숫자 줄: 0, k, 2k, ... 를 차례로 보며 쓸 수 있는 것만 뒤에 붙인다        var labels = new StringBuilder();        int count = 0;        for (int m = 0; m <= n; m += k)        {            string t = m.ToString();            bool fits = m + t.Length - 1 <= n; // 마지막 칸이 n을 넘지 않는다            bool gap = labels.Length == 0 || m > labels.Length; // 앞 숫자와 빈칸이 하나 이상            if (fits && gap)            {                labels.Append(' ', m - labels.Length).Append(t);                count++;            }        }        Console.WriteLine(marks);        Console.WriteLine(labels);        Console.WriteLine(count);    }}
C++main.cpp · 26 行
#include <iostream>#include <string> int main() {    int n, k;    std::cin >> n >> k;     // 눈금 줄: 칸 0 ~ n, 모두 n + 1글자    std::string marks;    for (int c = 0; c <= n; c++) marks += c % k == 0 ? '|' : '.';     // 숫자 줄: 0, k, 2k, ... 를 차례로 보며 쓸 수 있는 것만 뒤에 붙인다    std::string labels;    int count = 0;    for (int m = 0; m <= n; m += k) {        std::string t = std::to_string(m);        int len = static_cast<int>(labels.size()); // size()는 부호 없는 수라 int로 바꿔 비교한다        bool fits = m + static_cast<int>(t.size()) - 1 <= n; // 마지막 칸이 n을 넘지 않는다        bool gap = len == 0 || m > len;                      // 앞 숫자와 빈칸이 하나 이상 있다        if (fits && gap) {            labels += std::string(m - len, ' ') + t;            count++;        }    }    std::cout << marks << '\n' << labels << '\n' << count << '\n';}
Cmain.c · 33 行
#include <stdio.h>#include <string.h> #define MAXN 5000 int main(void) {    int n, k;    scanf("%d %d", &n, &k);     /* 눈금 줄: 칸 0 ~ n, 모두 n + 1글자 */    static char marks[MAXN + 2];    for (int c = 0; c <= n; c++) marks[c] = c % k == 0 ? '|' : '.';    marks[n + 1] = '\0';     /* 숫자 줄: 0, k, 2k, ... 를 차례로 보며 쓸 수 있는 것만 뒤에 붙인다 */    static char labels[MAXN + 2];    int len = 0, count = 0;    char t[8];    for (int m = 0; m <= n; m += k) {        int w = sprintf(t, "%d", m);          /* sprintf는 쓴 글자 수를 돌려준다 */        int fits = m + w - 1 <= n;            /* 마지막 칸이 n을 넘지 않는다 */        int gap = len == 0 || m > len;        /* 앞 숫자와 빈칸이 하나 이상 있다 */        if (fits && gap) {            while (len < m) labels[len++] = ' ';            memcpy(labels + len, t, w);            len += w;            count++;        }    }    labels[len] = '\0';    printf("%s\n%s\n%d\n", marks, labels, count);    return 0;}
Craykonmain.cray · 35 行
v = コンソール.数読み();n = v[0];k = v[1]; // 눈금 줄: 칸 0 ~ n, 모두 n + 1글자눈금 = "";繰り返す (c = 0; c <= n; c += 1){    もし (c % k == 0)    {        눈금 += "|";    }    でなければ    {        눈금 += ".";    }} // 숫자 줄: 0, k, 2k, ... 를 차례로 보며 쓸 수 있는 것만 뒤에 붙인다숫자줄 = "";개수 = 0;繰り返す (m = 0; m <= n; m += k){    t = "" + m;    맞음 = m + t.長さ - 1 <= n; // 마지막 칸이 n을 넘지 않는다    띄움 = 숫자줄 == "" || m > 숫자줄.長さ; // 앞 숫자와 빈칸이 하나 이상 있다    もし (맞음 && 띄움)    {        숫자줄 += " ".繰り返し(m - 숫자줄.長さ) + t;        개수 += 1;    }}コンソール.書く(눈금);コンソール.書く(숫자줄);コンソール.書く(개수);