{ }codelper
for ループ飛ばしながらループ2次元リストP0335

パッチワークの斜めの列を何本かおきに取り出す

縦 R 行、横 C 列のパッチワークの各マスに英小文字が一つ書かれている。
行は上から 1 番、列は左から 1 番である。
(行番号 + 列番号 − 1)が d であるマスの集まりを d 番の斜線と呼ぶ。
斜線は 1 番から R + C − 1 番まである。
斜線を一本読むときは、その斜線の最も下の行にあるマスから上の行へ進みながら(左下から右上へ)文字をつなげる。
a 番の斜線から s 本ずつ飛ばして、a, a + s, a + 2 × s, … のうち b 以下の番号の斜線を順に読む。
一行目に R と C が空白一つで区切って与えられる。
続く R 行に、行ごとに長さ C の文字列が与えられる。
最後の行に a、b、s が空白一つで区切って与えられる。
読んだ斜線ごとに、つなげた文字列を一行に、番号の小さい斜線から出力せよ。
斜線にないマスは見ずに、繰り返しの始まりと終わりを計算して、その斜線のマスだけを回ってみよう。

入力
1 ≤ R ≤ 100。
1 ≤ C ≤ 100。
1 ≤ a ≤ b ≤ R + C − 1。
1 ≤ s ≤ R + C − 1。
文字はすべて英小文字
出力
読んだ斜線ごとに一行
例 1 · stdin
3 4
abcd
efgh
ijkl
2 6 2
stdout
eb
jgd
l
例 2 · stdin
4 2
ab
cd
ef
gh
1 5 1
stdout
a
cb
ed
gf
h
main.py
入力(stdin)

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

$ 準備完了

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

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

飛ばしながら数える繰り返しは、Python では for d in range(a, b + 1, s)、ほかの言語では for (d = a; d <= b; d += s)(Craykon は 繰り返す (d = a; d <= b; d += s))と書きます。
一本の斜線の中では、0 から数えた行 r と列 c が r + c = d − 1 なので、行を min(d - 1, R - 1) から max(0, d - C) まで逆に数え、列は d - 1 - r で求めます。
逆に数えるのは Python range(low, high - 1, -1)、ほかは for (r = low; r >= high; r--) です。
小さい方・大きい方は Python min/max、JS・TS・Java Math.min/Math.max、C# Math.Min/Math.Max、C++ std::min/std::max、Craykon 数学.小さい方/数学.大きい方 で、C は a < b ? a : b と書きます。
落とし穴 ① Python の range は終わりの値を含みません。
range(a, b, s) と書くと、(b − a) が s の倍数のとき b 番の斜線が抜けます(一つ目の例で最後の行 l が出ません)。
逆に数えるときも同じで、range(low, high, -1) は最も上のマスを落とします(一つ目の例が e、jg と何もない行になります)。
② 最も上の行は横の列数 C で決まります。
max(0, d - R) のように R を使うと正方形でしか合わず、横に長いパッチワークでは文字が抜け、縦に長いパッチワークでは存在しないマスを読みます。
③ 範囲を計算せずにすべての行を回って c < C だけを確かめると、c が負のマスが残ります。
Python は grid[r][-1] をエラーなしにその行の最後の文字として読むので、一つ目の例の一行目が leb になります。
JS・TS は undefined を返すので "undefined" がつながり、Java・C# は例外で止まり、Craykon も「文字列には 0〜3 番しかない」と止まり、C・C++ は配列の外を読みます。
④ 向きを逆に(上の行から下の行へ)回ると文字列が逆になります。
⑤ d += s の代わりに d++ と書くと、飛ばさずにすべての斜線が出ます。

Pythonmain.py · 20 行
import sys  def main():    lines = sys.stdin.read().split()    R, C = int(lines[0]), int(lines[1])    grid = lines[2:2 + R]    a, b, s = (int(x) for x in lines[2 + R:5 + R])     out = []    for d in range(a, b + 1, s):  # range는 끝을 넣지 않으므로 b + 1까지        # d번 사선의 칸은 (0부터 센) 줄 r + 칸 c = d - 1을 만족한다        low = min(d - 1, R - 1)  # 가장 아래 줄        high = max(0, d - C)  # 가장 위 줄        # 거꾸로 셀 때도 끝은 들어가지 않는다: high - 1까지        out.append("".join(grid[r][d - 1 - r] for r in range(low, high - 1, -1)))    print("\n".join(out))  main()
JavaScriptmain.js · 18 行
const lines = require("fs").readFileSync(0, "utf8").split(/\s+/).filter((s) => s !== "");const R = Number(lines[0]);const C = Number(lines[1]);const grid = lines.slice(2, 2 + R);const [a, b, s] = lines.slice(2 + R, 5 + R).map(Number); const out = [];for (let d = a; d <= b; d += s) {  // d번 사선의 칸은 (0부터 센) 줄 r + 칸 c = d - 1을 만족한다  const low = Math.min(d - 1, R - 1); // 가장 아래 줄  const high = Math.max(0, d - C); // 가장 위 줄  let line = "";  for (let r = low; r >= high; r--) {    line += grid[r][d - 1 - r];  }  out.push(line);}console.log(out.join("\n"));
TypeScriptmain.ts · 22 行
import { readFileSync } from "node:fs"; const lines: string[] = readFileSync(0, "utf8").split(/\s+/).filter((s) => s !== "");const R: number = Number(lines[0]);const C: number = Number(lines[1]);const grid: string[] = lines.slice(2, 2 + R);const a: number = Number(lines[2 + R]);const b: number = Number(lines[3 + R]);const s: number = Number(lines[4 + R]); const out: string[] = [];for (let d = a; d <= b; d += s) {  // d번 사선의 칸은 (0부터 센) 줄 r + 칸 c = d - 1을 만족한다  const low: number = Math.min(d - 1, R - 1); // 가장 아래 줄  const high: number = Math.max(0, d - C); // 가장 위 줄  let line = "";  for (let r = low; r >= high; r--) {    line += grid[r][d - 1 - r];  }  out.push(line);}console.log(out.join("\n"));
JavaMain.java · 30 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.StringTokenizer; public class Main {    public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        StringTokenizer st = new StringTokenizer(in.readLine());        int R = Integer.parseInt(st.nextToken()), C = Integer.parseInt(st.nextToken());        String[] grid = new String[R];        for (int r = 0; r < R; r++) {            grid[r] = in.readLine().trim();        }        st = new StringTokenizer(in.readLine());        int a = Integer.parseInt(st.nextToken()), b = Integer.parseInt(st.nextToken()), s = Integer.parseInt(st.nextToken());         StringBuilder out = new StringBuilder();        for (int d = a; d <= b; d += s) {            // d번 사선의 칸은 (0부터 센) 줄 r + 칸 c = d - 1을 만족한다            int low = Math.min(d - 1, R - 1); // 가장 아래 줄            int high = Math.max(0, d - C); // 가장 위 줄            for (int r = low; r >= high; r--) {                out.append(grid[r].charAt(d - 1 - r));            }            out.append('\n');        }        System.out.print(out);    }}
C#Main.cs · 32 行
using System;using System.Text; class Program{    static void Main()    {        string[] head = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);        int R = int.Parse(head[0]), C = int.Parse(head[1]);        string[] grid = new string[R];        for (int r = 0; r < R; r++)        {            grid[r] = Console.ReadLine().Trim();        }        string[] tail = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);        int a = int.Parse(tail[0]), b = int.Parse(tail[1]), s = int.Parse(tail[2]);         var sb = new StringBuilder();        for (int d = a; d <= b; d += s)        {            // d번 사선의 칸은 (0부터 센) 줄 r + 칸 c = d - 1을 만족한다            int low = Math.Min(d - 1, R - 1); // 가장 아래 줄            int high = Math.Max(0, d - C); // 가장 위 줄            for (int r = low; r >= high; r--)            {                sb.Append(grid[r][d - 1 - r]);            }            sb.Append('\n');        }        Console.Write(sb);    }}
C++main.cpp · 26 行
#include <algorithm>#include <iostream>#include <string>#include <vector> int main() {    std::ios::sync_with_stdio(false);    std::cin.tie(nullptr);    int R, C;    std::cin >> R >> C;    std::vector<std::string> grid(R);    for (auto& row : grid) std::cin >> row;    int a, b, s;    std::cin >> a >> b >> s;     for (int d = a; d <= b; d += s) {        // d번 사선의 칸은 (0부터 센) 줄 r + 칸 c = d - 1을 만족한다        int low = std::min(d - 1, R - 1); // 가장 아래 줄        int high = std::max(0, d - C);    // 가장 위 줄        std::string line;        for (int r = low; r >= high; r--) {            line += grid[r][d - 1 - r];        }        std::cout << line << '\n';    }}
Cmain.c · 23 行
#include <stdio.h> static char grid[100][101]; int main(void) {    int R, C, a, b, s;    scanf("%d %d", &R, &C);    for (int r = 0; r < R; r++) {        scanf("%100s", grid[r]);    }    scanf("%d %d %d", &a, &b, &s);     for (int d = a; d <= b; d += s) {        /* d번 사선의 칸은 (0부터 센) 줄 r + 칸 c = d - 1을 만족한다 */        int low = d - 1 < R - 1 ? d - 1 : R - 1; /* 가장 아래 줄 */        int high = d - C > 0 ? d - C : 0;        /* 가장 위 줄 */        for (int r = low; r >= high; r--) {            putchar(grid[r][d - 1 - r]);        }        putchar('\n');    }    return 0;}
Craykonmain.cray · 25 行
head = コンソール.数読み();R = head[0];C = head[1];grid = [];繰り返す (r = 0; r < R; r++){    grid.追加(コンソール.行読み().空白除去());}tail = コンソール.数読み();a = tail[0];b = tail[1];s = tail[2]; 繰り返す (d = a; d <= b; d += s){    // d번 사선의 칸은 (0부터 센) 줄 r + 칸 c = d - 1을 만족한다    low = 数学.小さい方(d - 1, R - 1);    high = 数学.大きい方(0, d - C);    line = "";    繰り返す (r = low; r >= high; r--)    {        line += grid[r][d - 1 - r];    }    コンソール.書く(line);}