アルゴリズム概要

文字列を指定された行数のジグザグパターンで配置し、各行を左から右へ読み出して連結した文字列を返す問題です。

問題例

入力: s = "PAYPALISHIRING", numRows = 3

P   A   H   N
A P L S I I G
Y   I   R
      

出力: "PAHNAPLSIIGYIR"

制約

戦略

主要ポイント

時間計算量: O(n) - 各文字を1回ずつ走査

空間計算量: O(1) - 出力バッファを除く補助領域は定数個

最適化手法: ローカル変数束縛、固定長リスト、ビットシフト演算

ステップバイステップ解説

Python実装

from __future__ import annotations
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    pass


class Solution:
    """
    LeetCode 6. Zigzag Conversion
    - 競技向け: convert() は高速版を直接呼び出し
    - 実務向け: 必要に応じて _validate_inputs() を有効化
    """

    def convert(self, s: str, numRows: int) -> str:
        """
        文字列を numRows 行のジグザグ配置で並べ替え、行ごとに読み出す。

        Args:
            s: 変換対象の文字列(長さ 1..1000)
            numRows: 行数(1..1000)

        Returns:
            変換後の文字列

        Time: O(n), Space: O(1) (出力バッファ除く)
        """
        n: int = len(s)

        # 基底条件: ジグザグ不要
        if numRows == 1 or numRows >= n:
            return s

        # 周期: ジグザグの1サイクル = 2*(numRows-1)
        cycle: int = (numRows - 1) * 2
        out: list[str] = [""] * n
        k: int = 0  # 出力リストの書き込み位置

        # ローカル束縛で属性アクセスを削減
        _s = s
        _n = n
        _out = out
        _cycle = cycle
        last_row = numRows - 1

        for row in range(numRows):
            i = row  # 現在行の縦成分の開始インデックス

            if row == 0 or row == last_row:
                # 端行: 斜め成分なし、周期刻みで縦成分のみ
                while i < _n:
                    _out[k] = _s[i]
                    k += 1
                    i += _cycle
            else:
                # 中間行: 縦成分 + 斜め成分
                step_diag = _cycle - (row << 1)  # cycle - 2*row
                while i < _n:
                    # 縦成分
                    _out[k] = _s[i]
                    k += 1
                    # 斜め成分(範囲内なら追加)
                    diag_idx = i + step_diag
                    if diag_idx < _n:
                        _out[k] = _s[diag_idx]
                        k += 1
                    i += _cycle

        return "".join(_out)

フローチャート

開始 numRows == 1 または numRows ≥ n はい 元の文字列を返却 いいえ 周期計算 cycle = 2*(numRows-1) 出力リスト初期化 out = [""] * n 各行を走査 row = 0 to numRows-1 端行か? (row==0 or row==last) はい 縦成分のみ i += cycle いいえ 縦+斜め成分 両方追加 全行完了 文字列結合 "".join(out) 終了

フローの説明:
1. 基底条件: numRows が 1 または n 以上の場合、ジグザグ不要なので元の文字列を返却
2. 周期計算: cycle = 2*(numRows-1) でジグザグの周期を計算
3. 行別走査: 各行について、端行(0行目、最終行)は縦成分のみ、中間行は縦+斜め成分を追加
4. 結合: 出力リストを文字列に結合して返却

計算量分析

項目 備考
時間計算量 O(n) 各文字を1回ずつ走査
空間計算量 O(1) 出力バッファを除く補助領域は定数個
最適化ポイント ローカル変数束縛、固定長リスト、ビットシフト演算

代替手法との比較

アプローチ 時間 空間 評価
周期式・直接抽出(本実装) O(n) O(1) ✓ 最適
行番号タグ付けソート O(n log n) O(n) 不要に重い
2D配置して読み出し O(n²) O(n²) 無駄セル多数で非現実的