{ }codelper
部分文字列入れ子の条件P0274

Python 式 a:b:c スライスをまねる

文字列 s と切り出しの質問 q 個が与えられる。
質問ごとに Python の s[a:b:c] と同じ規則で切り出した文字列を出力せよ。
s の長さを n とし、文字の位置は先頭を 0 として 0 から n − 1 まで数える。
質問は a:b または a:b:c の形である。
a、b、c はそれぞれ整数か空である。
c がないか空なら、歩幅 c は 1 である。
c が正のとき:a が空なら開始は 0、b が空なら終わりは n である。
与えられた a は負なら n を足す。
その結果が 0 より小さければ 0 に、n より大きければ n に変えたものが開始である。
b も同じ方法で終わりを決める。
そのあと位置 開始、開始 + c、開始 + 2c、… の文字を、位置が終わりより小さい間、順につなぐ。
c が負のとき:a が空なら開始は n − 1、b が空なら終わりは −1 である(0 番の文字まで入れるという意味)。
与えられた a は負なら n を足す。
その結果が 0 より小さければ −1 に、n − 1 より大きければ n − 1 に変えたものが開始である。
b も同じ方法で終わりを決める。
そのあと位置 開始、開始 + c、開始 + 2c、… の文字を、位置が終わりより大きい間、順につなぐ。
一行目に s、二行目に q、続く q 行に質問が一つずつ与えられる。
質問ごとに切り出した文字列を一行に出力せよ。
空なら - を出力せよ。

入力
s は英小文字と数字からなる 1~200 文字の文字列。
1 ≤ q ≤ 400。
質問の a、b、c は空でなければ -1,000 以上 1,000 以下の整数で、c は 0 ではない。
質問に空白はない
出力
q 行
例 1 · stdin
codelper
8
1:4
:3
-3:
::-1
6:1:-2
5:2
::3
-100:100
stdout
ode
cod
per
repledoc
eld
-
cee
codelper
例 2 · stdin
abcdef
8
::-1
3::-1
:2:-1
-1:-7:-1
-7::-1
10:0:-1
::-1000
1:-1:1000
stdout
fedcba
dcba
fed
fedcba
-
fedcb
f
b
main.py
入力(stdin)

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

$ 準備完了

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

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

歩幅と負の位置を両方 s[a:b:c] で受け付けるのは Python だけです。
空の部分は None なので、s[slice(None, None, -1)] のように渡せば Python が歩幅の符号に合わせて既定値を決めます。
ほかの言語は自分で作ります:JS・TS の slice(a, b) は負の位置を受け付けますが歩幅がなく(substring は負を 0 とみなします)、Java substring(a, b)・C# Substring(開始, 長さ)・Craykon 切り出す(開始, 個数) は範囲を超えるとエラーです。
C++ substr(pos, len) は長さのはみ出しだけ許し、pos が長さを超えると out_of_range です。
落とし穴 ① 歩幅が負のとき、空の終わりは 0 ではなく −1 です。
0 にすると ::-1 で 0 番の文字が抜けます。
だから Python でも s[a:b:c] の空の部分を 0 や len(s) で埋めると間違えます(s[0:8:-1] は空文字列)。
② 負の位置はまず n を足し、それでも外なら寄せます。
歩幅が負なら 0 より下は 0 ではなく −1 に寄せるので、-100::-1 は空文字列です。
③ 歩幅が負なら開始の上限は n ではなく n − 1 です。
n まで許すと外を読みます(Java・C#・Craykon はエラー、C は未定義動作)。
④ 分割で空の部分が消えないようにします:Java の split(":") は末尾の空の部分を捨てるので split(":", -1) を使います。
C の strtok も続いた :: を一つとみなします。
C++ std::optional、TS string | undefined で「空」を 0 と区別して表せます。

Pythonmain.py · 11 行
import sys lines = sys.stdin.read().split("\n")s = lines[0].strip()q = int(lines[1])out = []for line in lines[2:2 + q]:    # 빈 칸은 None: s[None:None:-1]처럼 기본값을 Python이 정한다(0을 넣으면 안 된다)    parts = [int(p) if p else None for p in line.strip().split(":")]    out.append(s[slice(*parts)] or "-")print("\n".join(out))
JavaScriptmain.js · 29 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const s = lines[0].trim();const n = s.length;const q = Number(lines[1]); // 음수 자리는 n을 더하고, 그래도 밖이면 [lo, hi] 안으로 당긴다function fit(x, lo, hi) {  if (x < 0) x += n;  return Math.min(Math.max(x, lo), hi);} function pySlice(text) {  const [a, b, c] = text.trim().split(":"); // c가 없으면 undefined  const step = c ? Number(c) : 1;  let out = "";  if (step > 0) {    const start = a ? fit(Number(a), 0, n) : 0;    const stop = b ? fit(Number(b), 0, n) : n;    for (let i = start; i < stop; i += step) out += s[i];  } else {    // 거꾸로 갈 때 빈 끝은 0번 글자까지 넣는다는 뜻이라 -1로 둔다    const start = a ? fit(Number(a), -1, n - 1) : n - 1;    const stop = b ? fit(Number(b), -1, n - 1) : -1;    for (let i = start; i > stop; i += step) out += s[i];  }  return out || "-";} console.log(lines.slice(2, 2 + q).map(pySlice).join("\n"));
TypeScriptmain.ts · 31 行
import { readFileSync } from "node:fs"; const lines: string[] = readFileSync(0, "utf8").split("\n");const s = lines[0].trim();const n = s.length;const q = Number(lines[1]); // 음수 자리는 n을 더하고, 그래도 밖이면 [lo, hi] 안으로 당긴다function fit(x: number, lo: number, hi: number): number {  if (x < 0) x += n;  return Math.min(Math.max(x, lo), hi);} function pySlice(text: string): string {  const [a, b, c]: (string | undefined)[] = text.trim().split(":"); // c가 없으면 undefined  const step = c ? Number(c) : 1;  let out = "";  if (step > 0) {    const start = a ? fit(Number(a), 0, n) : 0;    const stop = b ? fit(Number(b), 0, n) : n;    for (let i = start; i < stop; i += step) out += s[i];  } else {    // 거꾸로 갈 때 빈 끝은 0번 글자까지 넣는다는 뜻이라 -1로 둔다    const start = a ? fit(Number(a), -1, n - 1) : n - 1;    const stop = b ? fit(Number(b), -1, n - 1) : -1;    for (let i = start; i > stop; i += step) out += s[i];  }  return out || "-";} console.log(lines.slice(2, 2 + q).map(pySlice).join("\n"));
JavaMain.java · 42 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader; public class Main {    static String s;    static int n;     // 음수 자리는 n을 더하고, 그래도 밖이면 [lo, hi] 안으로 당긴다    static int fit(int x, int lo, int hi) {        if (x < 0) x += n;        return Math.min(Math.max(x, lo), hi);    }     static String pySlice(String text) {        String[] p = text.split(":", -1); // -1: 끝의 빈 칸도 버리지 않는다        boolean hasA = !p[0].isEmpty(), hasB = !p[1].isEmpty();        int step = p.length == 3 && !p[2].isEmpty() ? Integer.parseInt(p[2]) : 1;        StringBuilder out = new StringBuilder();        if (step > 0) {            int start = hasA ? fit(Integer.parseInt(p[0]), 0, n) : 0;            int stop = hasB ? fit(Integer.parseInt(p[1]), 0, n) : n;            for (int i = start; i < stop; i += step) out.append(s.charAt(i));        } else {            // 거꾸로 갈 때 빈 끝은 0번 글자까지 넣는다는 뜻이라 -1로 둔다            int start = hasA ? fit(Integer.parseInt(p[0]), -1, n - 1) : n - 1;            int stop = hasB ? fit(Integer.parseInt(p[1]), -1, n - 1) : -1;            for (int i = start; i > stop; i += step) out.append(s.charAt(i));        }        return out.length() > 0 ? out.toString() : "-";    }     public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        s = in.readLine().trim();        n = s.length();        int q = Integer.parseInt(in.readLine().trim());        StringBuilder out = new StringBuilder();        for (int t = 0; t < q; t++) out.append(pySlice(in.readLine().trim())).append('\n');        System.out.print(out);    }}
C#Main.cs · 47 行
using System;using System.Text; class Program{    static string s = "";    static int n;     // 음수 자리는 n을 더하고, 그래도 밖이면 [lo, hi] 안으로 당긴다    static int Fit(int x, int lo, int hi)    {        if (x < 0) x += n;        return Math.Clamp(x, lo, hi);    }     static string PySlice(string text)    {        string[] p = text.Split(':'); // C#의 Split은 빈 칸도 남긴다        bool hasA = p[0] != "", hasB = p[1] != "";        int step = p.Length == 3 && p[2] != "" ? int.Parse(p[2]) : 1;        var sb = new StringBuilder();        if (step > 0)        {            int start = hasA ? Fit(int.Parse(p[0]), 0, n) : 0;            int stop = hasB ? Fit(int.Parse(p[1]), 0, n) : n;            for (int i = start; i < stop; i += step) sb.Append(s[i]);        }        else        {            // 거꾸로 갈 때 빈 끝은 0번 글자까지 넣는다는 뜻이라 -1로 둔다            int start = hasA ? Fit(int.Parse(p[0]), -1, n - 1) : n - 1;            int stop = hasB ? Fit(int.Parse(p[1]), -1, n - 1) : -1;            for (int i = start; i > stop; i += step) sb.Append(s[i]);        }        return sb.Length > 0 ? sb.ToString() : "-";    }     static void Main()    {        s = Console.ReadLine().Trim();        n = s.Length;        int q = int.Parse(Console.ReadLine().Trim());        var output = new StringBuilder();        for (int t = 0; t < q; t++) output.Append(PySlice(Console.ReadLine().Trim())).Append('\n');        Console.Write(output);    }}
C++main.cpp · 53 行
#include <algorithm>#include <iostream>#include <optional>#include <string>#include <vector> std::string s;int n; // 음수 자리는 n을 더하고, 그래도 밖이면 [lo, hi] 안으로 당긴다int fit(int x, int lo, int hi) {    if (x < 0) x += n;    return std::clamp(x, lo, hi);} std::string pySlice(const std::string &text) {    std::vector<std::optional<int>> p; // 빈 칸은 값이 없는 optional    std::size_t from = 0;    while (true) {        std::size_t colon = text.find(':', from);        std::string part = text.substr(from, colon == std::string::npos ? std::string::npos : colon - from);        p.push_back(part.empty() ? std::nullopt : std::optional<int>(std::stoi(part)));        if (colon == std::string::npos) break;        from = colon + 1;    }    int step = p.size() == 3 && p[2] ? *p[2] : 1;    std::string out;    if (step > 0) {        int start = p[0] ? fit(*p[0], 0, n) : 0;        int stop = p[1] ? fit(*p[1], 0, n) : n;        for (int i = start; i < stop; i += step) out += s[i];    } else {        // 거꾸로 갈 때 빈 끝은 0번 글자까지 넣는다는 뜻이라 -1로 둔다        int start = p[0] ? fit(*p[0], -1, n - 1) : n - 1;        int stop = p[1] ? fit(*p[1], -1, n - 1) : -1;        for (int i = start; i > stop; i += step) out += s[i];    }    return out.empty() ? "-" : out;} int main() {    int q;    std::cin >> s >> q;    n = static_cast<int>(s.size());    std::string out;    for (int t = 0; t < q; t++) {        std::string line;        std::cin >> line;        out += pySlice(line) + '\n';    }    std::cout << out;    return 0;}
Cmain.c · 47 行
#include <stdio.h>#include <stdlib.h>#include <string.h> static char s[256];static int n; /* 음수 자리는 n을 더하고, 그래도 밖이면 [lo, hi] 안으로 당긴다 */static int fit(int x, int lo, int hi) {    if (x < 0) x += n;    return x < lo ? lo : x > hi ? hi : x;} int main(void) {    char line[64];    int q;    scanf("%255s %d", s, &q);    n = (int)strlen(s);    for (int t = 0; t < q; t++) {        scanf("%63s", line);        /* ':'로 칸을 나눈다. strtok는 빈 칸을 건너뛰어서 직접 나눈다 */        char *part[3] = {line, NULL, NULL};        int k = 1;        for (char *c = line; *c; c++)            if (*c == ':') {                *c = '\0';                part[k++] = c + 1;            }        int hasA = part[0][0] != '\0', hasB = part[1][0] != '\0';        int step = k == 3 && part[2][0] != '\0' ? atoi(part[2]) : 1;        char out[256];        int len = 0;        if (step > 0) {            int start = hasA ? fit(atoi(part[0]), 0, n) : 0;            int stop = hasB ? fit(atoi(part[1]), 0, n) : n;            for (int i = start; i < stop; i += step) out[len++] = s[i];        } else {            /* 거꾸로 갈 때 빈 끝은 0번 글자까지 넣는다는 뜻이라 -1로 둔다 */            int start = hasA ? fit(atoi(part[0]), -1, n - 1) : n - 1;            int stop = hasB ? fit(atoi(part[1]), -1, n - 1) : -1;            for (int i = start; i > stop; i += step) out[len++] = s[i];        }        out[len] = '\0';        puts(len > 0 ? out : "-");    }    return 0;}
Craykonmain.cray · 64 行
s = コンソール.行読み().空白除去();n = s.長さ;q = コンソール.行読み().数に(); // 음수 자리는 n을 더하고, 그래도 밖이면 [lo, hi] 안으로 당긴다맞추기(x, lo, hi){    もし (x < 0)    {        x = x + n;    }    返す 数学.小さい方(数学.大きい方(x, lo), hi);} 繰り返す (t = 0; t < q; t++){    p = コンソール.行読み().空白除去().分ける(":");    걸음 = 1;    もし (p.個数 == 3 && p[2] != "")    {        걸음 = p[2].数に();    }    답 = "";    もし (걸음 > 0)    {        시작 = 0;        끝 = n;        もし (p[0] != "")        {            시작 = 맞추기(p[0].数に(), 0, n);        }        もし (p[1] != "")        {            끝 = 맞추기(p[1].数に(), 0, n);        }        繰り返す (k = 시작; k < 끝; k = k + 걸음)        {            답 = 답 + s[k];        }    }    でなければ    {        // 거꾸로 갈 때 빈 끝은 0번 글자까지 넣는다는 뜻이라 -1로 둔다        시작 = n - 1;        끝 = -1;        もし (p[0] != "")        {            시작 = 맞추기(p[0].数に(), -1, n - 1);        }        もし (p[1] != "")        {            끝 = 맞추기(p[1].数に(), -1, n - 1);        }        繰り返す (k = 시작; k > 끝; k = k + 걸음)        {            답 = 답 + s[k];        }    }    もし (답 == "")    {        답 = "-";    }    コンソール.書く(답);}