Skip to content

Commit 6eb2d1b

Browse files
authored
Merge pull request #200 from myoshi2891/dev-from-macmini
Dev from macmini
2 parents 57cb6e0 + e11975c commit 6eb2d1b

8 files changed

Lines changed: 3309 additions & 852 deletions

File tree

Algorithm/TwoPointers/leetcode/67. Add Binary/Claude/README.md

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
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

@@ -35,22 +37,25 @@
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
5661
flowchart 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() を呼び出し
239254
bit = 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
249265
result = []
@@ -258,19 +274,22 @@ return out[start:].decode('ascii')
258274
```
259275

260276
### 3. 分岐の最小化
277+
261278
```python
262279
# 条件式でビット取得を統一
263280
ai = (a_bytes[i] & 1) if i >= 0 else 0
264281
bj = (b_bytes[j] & 1) if j >= 0 else 0
265282
```
266283

267284
### 4. 定数のローカル化
285+
268286
```python
269287
ZERO: Final[int] = 48 # グローバル参照を避ける
270288
out[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

Comments
 (0)