Skip to content

Commit b99dd73

Browse files
committed
leetcode 8. String to Integer (atoi)
1 parent 43adc62 commit b99dd73

5 files changed

Lines changed: 476 additions & 1 deletion

File tree

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
TypeScriptでの `myAtoi` 実装における**各処理ステップを図解**付きで詳細に解説します。例として、入力文字列が `" -042abc"` の場合を扱います。
2+
3+
---
4+
5+
## ✅ ステップ1: 空白のスキップ
6+
7+
```typescript
8+
while (i < n && s[i] === ' ') {
9+
i++;
10+
}
11+
```
12+
13+
### 🔍 処理前
14+
15+
```
16+
s = " -042abc"
17+
18+
i=0
19+
```
20+
21+
空白をスキップしていく:
22+
23+
```
24+
s = " -042abc"
25+
26+
i=3
27+
```
28+
29+
`i = 3` で空白スキップ完了。`s[3] = '-'`
30+
31+
---
32+
33+
## ✅ ステップ2: 符号の確認
34+
35+
```typescript
36+
if (i < n && (s[i] === '+' || s[i] === '-')) {
37+
if (s[i] === '-') sign = -1;
38+
i++;
39+
}
40+
```
41+
42+
### 🔍 処理前
43+
44+
```
45+
s = " -042abc"
46+
47+
i=3, s[i]='-'
48+
```
49+
50+
* `'-'` を発見 → `sign = -1`
51+
* `i++`
52+
53+
### 🔍 処理後
54+
55+
```
56+
s = " -042abc"
57+
58+
i=4, s[i]='0'
59+
sign = -1
60+
```
61+
62+
---
63+
64+
## ✅ ステップ3: 数字部分の読み取り
65+
66+
```typescript
67+
while (i < n && s[i] >= '0' && s[i] <= '9') {
68+
const digit = s[i].charCodeAt(0) - '0'.charCodeAt(0);
69+
result = result * 10 + digit;
70+
i++;
71+
}
72+
```
73+
74+
### 🧮 処理ステップ(ループ)
75+
76+
#### 1周目(s\[4] = '0')
77+
78+
```
79+
digit = 0
80+
result = 0 * 10 + 0 = 0
81+
i → 5
82+
```
83+
84+
#### 2周目(s\[5] = '4')
85+
86+
```
87+
digit = 4
88+
result = 0 * 10 + 4 = 4
89+
i → 6
90+
```
91+
92+
#### 3周目(s\[6] = '2')
93+
94+
```
95+
digit = 2
96+
result = 4 * 10 + 2 = 42
97+
i → 7
98+
```
99+
100+
#### 4周目(s\[7] = 'a')
101+
102+
* `s[7]` は数字でないため **ループ終了**
103+
104+
### 🔍 終了時
105+
106+
```
107+
result = 42
108+
sign = -1
109+
```
110+
111+
---
112+
113+
## ✅ ステップ4: オーバーフローの処理と返却
114+
115+
```typescript
116+
return result * sign;
117+
```
118+
119+
```
120+
result = 42
121+
sign = -1
122+
→ return -42
123+
```
124+
125+
---
126+
127+
## ✅ 最終結果
128+
129+
```
130+
入力: " -042abc"
131+
出力: -42
132+
```
133+
134+
---
135+
136+
## 🧠 全体のフローチャート風まとめ
137+
138+
```
139+
入力: " -042abc"
140+
141+
↓ ステップ1: 空白スキップ
142+
i = 0 → 3
143+
144+
↓ ステップ2: 符号チェック
145+
s[3] = '-' → sign = -1, i = 4
146+
147+
↓ ステップ3: 数字読み取り
148+
s[4] = '0' → result = 0
149+
s[5] = '4' → result = 4
150+
s[6] = '2' → result = 42
151+
s[7] = 'a' → 終了
152+
153+
↓ ステップ4: 結果に符号適用
154+
-1 × 42 = -42
155+
156+
→ ✅ 出力: -42
157+
```
158+
159+
---
160+
161+
## ✅ 他の例も図で簡潔に
162+
163+
### 例1: `"words and 987"`
164+
165+
```
166+
s = "words and 987"
167+
s[0] = 'w'(非数字)→ 数字読み取り前に終了 → return 0
168+
```
169+
170+
---
171+
172+
### 例2: `"91283472332"`(オーバーフロー)
173+
174+
```
175+
result = 91283472332 > INT_MAX
176+
→ return INT_MAX = 2147483647
177+
```
178+
179+
---
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// 以下は、指定された仕様に従って `myAtoi` 関数を TypeScript で実装したものです。
2+
function myAtoi(s: string): number {
3+
const INT_MIN = -(2 ** 31);
4+
const INT_MAX = 2 ** 31 - 1;
5+
6+
let i = 0;
7+
const n = s.length;
8+
9+
// 1. 空白をスキップ
10+
while (i < n && s[i] === ' ') {
11+
i++;
12+
}
13+
14+
// 2. 符号の確認
15+
let sign = 1;
16+
if (i < n && (s[i] === '+' || s[i] === '-')) {
17+
if (s[i] === '-') sign = -1;
18+
i++;
19+
}
20+
21+
// 3. 数字を読み取る
22+
let result = 0;
23+
while (i < n && s[i] >= '0' && s[i] <= '9') {
24+
const digit = s[i].charCodeAt(0) - '0'.charCodeAt(0);
25+
26+
// 4. オーバーフロー確認
27+
if (result > Math.floor(INT_MAX / 10) ||
28+
(result === Math.floor(INT_MAX / 10) && digit > INT_MAX % 10)) {
29+
return sign === 1 ? INT_MAX : INT_MIN;
30+
}
31+
32+
result = result * 10 + digit;
33+
i++;
34+
}
35+
36+
return result * sign;
37+
}
38+
39+
// ### ✅ 処理フローの図解説明
40+
41+
// #### 例: `" -042"` の場合
42+
43+
// | ステップ | 処理 | 状態 |
44+
// | ---- | ------------------------ | ------------------ |
45+
// | 1 | 空白をスキップ (`i=0→3`) | `i=3`, `s[i]='-'` |
46+
// | 2 | `'-'`を見つけた → `sign = -1` | `i=4`, `sign = -1` |
47+
// | 3 | `0` → `4` → `2` と読み取る | `result = 42` |
48+
// | 4 | 範囲内 → そのまま返す | `return -42` |
49+
50+
// ---
51+
52+
// ### ✅ 制約への対応
53+
54+
// * 空文字列や非数字始まり → `0` を返す
55+
// * 数値部分が 32-bit の範囲外 → オーバーフロー対策済み
56+
// * 入力が `"words and 987"` のようなケース → 先頭が非数字 → `0` を返す
57+
58+
// ---
59+
60+
// ### ✅ テスト例
61+
62+
// ```typescript
63+
// console.log(myAtoi("42")); // 42
64+
// console.log(myAtoi(" -042")); // -42
65+
// console.log(myAtoi("1337c0d3")); // 1337
66+
// console.log(myAtoi("0-1")); // 0
67+
// console.log(myAtoi("words and 987")); // 0
68+
// console.log(myAtoi("-91283472332")); // -2147483648(下限に丸められる)
69+
// console.log(myAtoi("91283472332")); // 2147483647(上限に丸められる)
70+
// ```
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// ### 🔍 よくあるランタイムエラーの原因(Go)
2+
3+
// | 原因 | 内容 |
4+
// | ---------------- | ------------------------------------------------------- |
5+
// | ❌ インデックスエラー | `acc[L-1]` のように `L=1` のとき `acc[0]` にアクセスできない or スライス外参照 |
6+
// | ❌ スキャン不足 | 入力のスキャン回数が `Q` に足りていない(クエリが途中で終わる) |
7+
// | ❌ スペースのない入力の処理ミス | `bufio.Scanner` が 1行ずつ読み取る前提で、改行の無い入力に対応できていない |
8+
// | ❌ 空文字列の変換 | `strconv.Atoi("")` のように、空の文字列を数値に変換してエラー |
9+
10+
// ### ✅ 修正版 Go コード(安全対策込み)
11+
12+
package main
13+
14+
import (
15+
"bufio"
16+
"os"
17+
"strconv"
18+
"strings"
19+
)
20+
21+
func main() {
22+
reader := bufio.NewReader(os.Stdin)
23+
24+
// Read N
25+
line, _ := reader.ReadString('\n')
26+
N, _ := strconv.Atoi(strings.TrimSpace(line))
27+
28+
// Read A
29+
line, _ = reader.ReadString('\n')
30+
Astr := strings.Fields(line)
31+
A := make([]int, N+1) // 1-indexed
32+
for i := 1; i <= N; i++ {
33+
A[i], _ = strconv.Atoi(Astr[i-1])
34+
}
35+
36+
// Build prefix sum
37+
acc := make([]int, N+1)
38+
for i := 1; i <= N; i++ {
39+
acc[i] = acc[i-1] + A[i]
40+
}
41+
42+
// Read Q
43+
line, _ = reader.ReadString('\n')
44+
Q, _ := strconv.Atoi(strings.TrimSpace(line))
45+
46+
writer := bufio.NewWriter(os.Stdout)
47+
defer writer.Flush()
48+
49+
// Read and process queries
50+
for i := 0; i < Q; i++ {
51+
line, err := reader.ReadString('\n')
52+
if err != nil {
53+
break // 安全策
54+
}
55+
LR := strings.Fields(line)
56+
if len(LR) < 2 {
57+
continue // 不正入力回避
58+
}
59+
L, _ := strconv.Atoi(LR[0])
60+
R, _ := strconv.Atoi(LR[1])
61+
62+
if L < 1 || R > N || L > R {
63+
writer.WriteString("draw\n") // 安全なフォールバック
64+
continue
65+
}
66+
67+
ones := acc[R] - acc[L-1]
68+
total := R - L + 1
69+
zeros := total - ones
70+
71+
if ones > zeros {
72+
writer.WriteString("win\n")
73+
} else if zeros > ones {
74+
writer.WriteString("lose\n")
75+
} else {
76+
writer.WriteString("draw\n")
77+
}
78+
}
79+
}
80+
81+
// ## ✅ 修正ポイントまとめ
82+
83+
// | 修正箇所 | 説明 | | |
84+
// | --------------------------- | ----------------------- | - | ----------------------- |
85+
// | ✅ `bufio.NewReader` 使用 | `bufio.Scanner` の行制限を回避 | | |
86+
// | ✅ `strings.Fields` 使用 | 空白区切り入力を安全に処理 | | |
87+
// | ✅ `acc` と `A` を 1-indexed に | `acc[L-1]` アクセスを安全にするため | | |
88+
// | ✅ クエリ境界チェック | \`L < 1 | | R > N\` などの入力ミスで落ちないように |
89+
90+
// ---
91+
92+
// ## ✅ 確認テスト(例)
93+
94+
// ### 入力
95+
96+
// ```
97+
// 7
98+
// 0 1 1 0 1 0 0
99+
// 3
100+
// 2 5
101+
// 2 7
102+
// 5 7
103+
// ```
104+
105+
// ### 出力
106+
107+
// ```
108+
// win
109+
// draw
110+
// lose
111+
// ```
112+
113+
// ## ✅ 補足
114+
115+
// * 万が一、`LR` の長さが不足する場合(不完全な入力など)にも対応。
116+
// * クエリ数が `Q` 未満でもクラッシュしないように `err != nil` チェック。
117+

0 commit comments

Comments
 (0)