📋 アルゴリズム概要
問題: 数字文字列
s
が与えられ、1→A, 2→B, ..., 26→Z
のマッピングでデコードできる方法の総数を返します。
制約: 1桁は 1~9 のみ有効(0
単独は無効)、2桁は 10~26 のみ有効(先頭0は不可)。
戦略:
ローリング動的計画法を使用。各位置で「1桁として取る」「2桁として取る」の2通りを判定し、前2状態(prev2,
prev1)から現在の通り数を計算します。
✨ 最適化ポイント
-
配列不要: スカラー変数2個(
prev2,prev1)のみで O(1) 空間 - 早期終了: デコード不能を検知した時点で即座に 0 を返す
- 整数演算: 文字列スライス回避で高速化
🎯 ステップバイステップ解説
💻 Python コード実装
LeetCode 形式の Solution クラス。ローリングDP で O(n) 時間 / O(1) 空間を実現。
from __future__ import annotations
from typing import Final
class Solution:
"""
Decode Ways (LeetCode 91)
数字文字列を A-Z ('1'-'26') にデコードする方法の総数を返す。
アルゴリズム:
- ローリングDP (prev2, prev1) で O(n) 時間 / O(1) 追加メモリ
- 1桁: '1'..'9' が有効
- 2桁: '10'..'26' が有効
- '0' 単独や先頭0は無効
"""
def numDecodings(self, s: str) -> int:
"""
Args:
s: 数字のみから成る文字列(長さ 1..100)
Returns:
デコード方法の総数(不能なら 0)
Complexity:
Time: O(n), Space: O(1)
"""
n: Final[int] = len(s)
# 基底条件: 空文字列は不正
if n == 0:
return 0
# 先頭が '0' ならデコード不能
first_digit: int = ord(s[0]) - ord('0')
if first_digit == 0:
return 0
# DP初期値:
# prev2 = dp[-1] = 1 (空文字列の基数)
# prev1 = dp[0] = 1 (先頭1文字、'1'..'9' が確定)
prev2: int = 1
prev1: int = 1
# 位置 1 から n-1 まで処理
for i in range(1, n):
d1: int = ord(s[i]) - ord('0') # 現在の1桁
d0: int = ord(s[i - 1]) - ord('0') # 直前の1桁
cur: int = 0
# 遷移1: 1桁が有効 ('1'..'9')
if 1 <= d1 <= 9:
cur += prev1
# 遷移2: 2桁が有効 ('10'..'26')
two_digit: int = d0 * 10 + d1
if 10 <= two_digit <= 26:
cur += prev2
# どちらも不可 → デコード不能
if cur == 0:
return 0
# ローリング更新
prev2, prev1 = prev1, cur
return prev1
📊 視覚的フローチャート
アルゴリズムの処理フロー全体を俯瞰する静的図解。
各位置で1桁・2桁の有効性を判定し、通り数を累積。どちらも不可なら即座に 0 を返す。
💡 アルゴリズムの概要
動的計画法: 各位置での有効なデコード方法数を前の2つの位置から計算します。
1桁の場合: 現在の文字が1〜9なら、前の位置の方法数を加算します。
2桁の場合: 前の文字と現在の文字で10〜26の範囲なら、2つ前の位置の方法数を加算します。
無効な場合: 1桁も2桁も有効でない場合、デコード不可能として0を返します。
📝 例: "226"のデコード
方法1: BBF
2 → B, 2 → B, 6 → F
方法2: VF
22 → V, 6 → F
方法3: BZ
2 → B, 26 → Z
⚡ 計算量分析
⏱️ 時間計算量
O(n)
各文字を1回ずつ処理。各ステップは定数時間の演算(整数加算、比較、更新)のみ。
💾 空間計算量
O(1)
追加メモリはスカラー変数3個(prev2, prev1, cur)のみ。配列不要。
📋 アプローチ比較
| 手法 | 時間 | 空間 | 備考 |
|---|---|---|---|
| ローリングDP(採用) | O(n) | O(1) | 最小メモリ、実装簡潔 |
| 配列DP | O(n) | O(n) | 学習用に明快 |
| 再帰+メモ化 | O(n) | O(n) | スタック深度+キャッシュ |