1919** 問題** : 2つの2進数文字列 ` a ` と ` b ` が与えられたとき、それらの和を2進数文字列として返す。
2020
2121** 要件** :
22+
2223- 入力は ` '0' ` と ` '1' ` のみで構成される
2324- 長さは 1 ≤ len(a), len(b) ≤ 10⁴
2425- 先頭ゼロは存在しない(` "0" ` 自体を除く)
2526- 出力も2進数文字列として正しい形式(先頭ゼロなし)
2627
2728** 制約** :
29+
2830- 文字列長が最大1万桁なので、整数変換(` int(a, 2) ` )は可能だが非効率
2931- メモリ効率と実行速度の両立が求められる
3032
3537** 戦略** : Two-Pointer + Carry(桁上がり)を用いた末尾からの1パス走査
3638
3739** データ構造** :
40+
3841- 入力を ` bytes ` に変換(` encode('ascii') ` )してビット取得を高速化
3942- 出力用に固定長 ` bytearray ` を確保し、末尾から直接ASCII文字を書き込み
4043
4144** 計算量** :
45+
4246- ** 時間** : O(max(len(a), len(b))) = O(n)
4347- ** 空間** : O(1) 補助空間(出力バッファを除く)
4448
4549** メモリ最適化** :
50+
4651- ` reverse() ` や ` insert(0, ...) ` などの高コスト操作を回避
4752- 文字列の逐次結合(` += ` )を避け、最後に一度だけ ` decode('ascii') `
4853
4954---
5055
5156<h2 id =" figures " >図解</h2 >
5257
53- ### フローチャート
58+ ## フローチャート
5459
5560``` mermaid
5661flowchart TD
@@ -70,6 +75,7 @@ flowchart TD
7075```
7176
7277** 説明** :
78+
7379- 早期チェックで一方が ` "0" ` の場合は他方をそのまま返す
7480- 文字列を ` bytes ` に変換して各反復での ` ord() ` 呼び出しを排除
7581- 末尾(最下位桁)から順に加算とキャリーを処理
@@ -97,6 +103,7 @@ graph LR
97103```
98104
99105** 説明** :
106+
100107- 入力文字列を ` bytes ` に変換してビット取得を高速化
101108- Two-Pointerで末尾から走査し、各桁の和とキャリーを計算
102109- ` bytearray ` への直接書き込みでメモリ効率を最大化
@@ -107,18 +114,22 @@ graph LR
107114<h2 id =" correctness " >正しさのスケッチ</h2 >
108115
109116** 不変条件** :
117+
110118- 各反復で、位置 ` i ` , ` j ` , ` k ` に対して ` k ` 位置に正しい桁の結果が書き込まれる
111119- ` carry ` は常に 0 または 1 の値を持つ(2進数の桁上がりは最大1)
112120
113121** 網羅性** :
122+
114123- ` while i >= 0 or j >= 0 or carry ` により、すべての桁とキャリーを処理
115124- 短い方の文字列が終わっても、長い方とキャリーの処理を続行
116125
117126** 基底条件** :
127+
118128- ` i < 0 ` かつ ` j < 0 ` かつ ` carry == 0 ` で終了
119129- 最上位桁のキャリーも ` bytearray ` に書き込まれる
120130
121131** 終了性** :
132+
122133- 各反復で ` i ` , ` j ` , ` k ` が減少
123134- 有限長の入力に対して必ず終了
124135
@@ -127,6 +138,7 @@ graph LR
127138<h2 id =" complexity " >計算量</h2 >
128139
129140### 時間計算量
141+
130142** O(n)** ここで n = max(len(a), len(b))
131143
132144- ` encode('ascii') ` : O(len(a)) + O(len(b))
@@ -135,6 +147,7 @@ graph LR
135147- ` decode('ascii') ` : O(n)
136148
137149### 空間計算量
150+
138151** O(1) 補助空間** (出力バッファを除く)
139152
140153- ` a_bytes ` , ` b_bytes ` : 入力サイズに比例(O(n))だが、入力として必要
@@ -143,11 +156,11 @@ graph LR
143156
144157### アプローチ比較
145158
146- | アプローチ | 時間 | 空間 | 特徴 |
147- | ---------| -----| ----- | -----|
148- | ** Two-Pointer + bytearray(採用)** | O(n) | O(1) | 最速、メモリ効率最高 |
149- | list.append + reverse + join | O(n) | O(n) | 実装簡単だが中間配列必要 |
150- | int変換 + bin() | O(n) | O(n) | 大きい数で非効率、多倍長演算 |
159+ | アプローチ | 時間 | 空間 | 特徴 |
160+ | ----------------------------------- | ---- | ---- | ---------------------------- |
161+ | ** Two-Pointer + bytearray(採用)** | O(n) | O(1) | 最速、メモリ効率最高 |
162+ | list.append + reverse + join | O(n) | O(n) | 実装簡単だが中間配列必要 |
163+ | int変換 + bin() | O(n) | O(n) | 大きい数で非効率、多倍長演算 |
151164
152165---
153166
@@ -224,6 +237,7 @@ class Solution:
224237```
225238
226239** 実装のポイント** :
240+
227241- ` encode('ascii') ` で文字列を ` bytes ` に変換し、各反復での ` ord() ` を排除
228242- ` & 1 ` でビット取得('0'=48→0, '1'=49→1)
229243- ` bytearray ` への直接書き込みで中間文字列生成を回避
@@ -234,6 +248,7 @@ class Solution:
234248<h2 id =" cpython " >CPython最適化ポイント</h2 >
235249
236250### 1. ` bytes ` 変換による ` ord() ` 排除
251+
237252``` python
238253# 遅い: 各反復で ord() を呼び出し
239254bit = ord (a[i]) - ord (' 0' )
@@ -244,6 +259,7 @@ bit = a_bytes[i] & 1
244259```
245260
246261### 2. 固定長 ` bytearray ` への直接書き込み
262+
247263``` python
248264# 遅い: リスト append + reverse + join
249265result = []
@@ -258,19 +274,22 @@ return out[start:].decode('ascii')
258274```
259275
260276### 3. 分岐の最小化
277+
261278``` python
262279# 条件式でビット取得を統一
263280ai = (a_bytes[i] & 1 ) if i >= 0 else 0
264281bj = (b_bytes[j] & 1 ) if j >= 0 else 0
265282```
266283
267284### 4. 定数のローカル化
285+
268286``` python
269287ZERO : Final[int ] = 48 # グローバル参照を避ける
270288out[k] = ZERO + (s & 1 )
271289```
272290
273291### パフォーマンス結果
292+
274293- ** Runtime** : 0ms(100.00%)
275294- ** Memory** : 17.92MB(28.60%)
276295
@@ -281,21 +300,24 @@ out[k] = ZERO + (s & 1)
281300<h2 id =" edgecases " >エッジケースと検証観点</h2 >
282301
283302### 境界値
284- | ケース | 入力 | 期待出力 | 検証ポイント |
285- | -------| -----| ---------| ------------|
286- | 両方最小 | ` a="0" ` , ` b="0" ` | ` "0" ` | 早期リターン |
287- | 片方最小 | ` a="0" ` , ` b="1" ` | ` "1" ` | 早期リターン |
288- | 最小桁 | ` a="1" ` , ` b="1" ` | ` "10" ` | キャリー発生 |
289- | キャリー連鎖 | ` a="1111" ` , ` b="1" ` | ` "10000" ` | 全桁キャリー |
290- | 長さ差大 | ` a="1" ` , ` b="1010101" ` | ` "1010110" ` | 短い方の処理 |
291- | 最大長 | len(a)=10⁴, len(b)=10⁴ | 正しい和 | パフォーマンス |
303+
304+ | ケース | 入力 | 期待出力 | 検証ポイント |
305+ | ------------ | ---------------------- | ----------- | -------------- |
306+ | 両方最小 | ` a="0" ` , ` b="0" ` | ` "0" ` | 早期リターン |
307+ | 片方最小 | ` a="0" ` , ` b="1" ` | ` "1" ` | 早期リターン |
308+ | 最小桁 | ` a="1" ` , ` b="1" ` | ` "10" ` | キャリー発生 |
309+ | キャリー連鎖 | ` a="1111" ` , ` b="1" ` | ` "10000" ` | 全桁キャリー |
310+ | 長さ差大 | ` a="1" ` , ` b="1010101" ` | ` "1010110" ` | 短い方の処理 |
311+ | 最大長 | len(a)=10⁴, len(b)=10⁴ | 正しい和 | パフォーマンス |
292312
293313### 特殊ケース
314+
294315- ** キャリーのみ残る** : ` a="1" ` , ` b="1" ` → ` "10" `
295316- ** 一方が極端に短い** : ` a="1" ` , ` b="1"*10000 `
296317- ** 交互のビットパターン** : ` a="1010" ` , ` b="0101" ` → ` "1111" `
297318
298319### 型安全性
320+
299321- 入力は必ず ` str ` 型(LeetCodeの制約保証)
300322- 内部は ` bytes ` と ` int ` のみで処理
301323- pylance で型エラーなし
@@ -307,6 +329,7 @@ out[k] = ZERO + (s & 1)
307329### Q1: なぜ ` int(a, 2) + int(b, 2) ` を使わないのか?
308330
309331** A** : 文字列長が最大10⁴桁の場合、整数変換は以下の問題がある:
332+
310333- 多倍長演算のオーバーヘッド
311334- メモリ使用量の増加(大きな整数オブジェクト)
312335- ` bin() ` での文字列化コスト
@@ -320,6 +343,7 @@ Two-Pointer方式は O(n) の線形時間で、メモリも最小限。
320343### Q3: なぜ ` bytearray ` を使うのか?
321344
322345** A** :
346+
323347- ミュータブルなバイト列で、固定長バッファとして使用可能
324348- ASCII文字(数値)の直接書き込みが可能
325349- ` reverse() ` や文字列結合のコストを回避
@@ -328,6 +352,7 @@ Two-Pointer方式は O(n) の線形時間で、メモリも最小限。
328352### Q4: 入力検証は必要か?
329353
330354** A** : LeetCodeの制約は保証されているため、本実装では省略。業務コードでは以下を検証:
355+
331356- 型チェック(` isinstance(a, str) ` )
332357- 長さチェック(1 ≤ len ≤ 10⁴)
333358- 文字チェック('0'/'1'のみ)
@@ -340,6 +365,7 @@ Two-Pointer方式は O(n) の線形時間で、メモリも最小限。
340365### Q6: より速くする方法は?
341366
342367** A** :
368+
343369- 入力検証を完全に削除(LeetCodeでは不要)
344370- ローカル変数への関数バインド(` append = list.append ` )
345371- ただし、現在の実装で既に 0ms(100%)を達成しており、これ以上の改善は環境依存
0 commit comments