Skip to content

Commit 22f595b

Browse files
committed
leetcode 17. Letter Combinations of a Phone Number バックトラッキング(DFS探索)
1 parent efc2969 commit 22f595b

3 files changed

Lines changed: 346 additions & 0 deletions

File tree

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# 以下は、**LeetCode用の Python (CPython 3.11.4)** に準拠した
2+
# 「電話番号の文字列組み合わせ」問題の実装です。
3+
4+
# ---
5+
6+
# ## **実装内容**
7+
8+
# * **アルゴリズム**:バックトラッキング(DFS探索)
9+
# * **時間計算量**:
10+
# O(3^N × 4^M)
11+
12+
# * N:3文字キー(2,3,4,5,6,8)の個数
13+
# * M:4文字キー(7,9)の個数
14+
# * **空間計算量**:
15+
# O(出力サイズ + 再帰スタック)
16+
17+
# ---
18+
19+
# ## **Pythonコード(LeetCode提出形式)**
20+
21+
# ```python
22+
from typing import List
23+
24+
class Solution:
25+
def letterCombinations(self, digits: str) -> List[str]:
26+
"""
27+
与えられた数字文字列から、考えられる全ての文字列の組み合わせを返す
28+
29+
Parameters
30+
----------
31+
digits : str
32+
電話番号を表す数字文字列 (0 <= len(digits) <= 4)
33+
34+
Returns
35+
-------
36+
List[str]
37+
数字文字列から作成される全ての文字列の組み合わせ
38+
39+
時間計算量: O(3^N × 4^M)
40+
空間計算量: O(組み合わせ数 + 再帰スタック)
41+
"""
42+
if not digits:
43+
return []
44+
45+
phone_map: dict[str, str] = {
46+
"2": "abc", "3": "def", "4": "ghi",
47+
"5": "jkl", "6": "mno", "7": "pqrs",
48+
"8": "tuv", "9": "wxyz"
49+
}
50+
51+
res: List[str] = []
52+
53+
def backtrack(index: int, path: str) -> None:
54+
"""
55+
再帰的に文字列の組み合わせを構築する
56+
57+
Parameters
58+
----------
59+
index : int
60+
digits内の現在の桁位置
61+
path : str
62+
現在までの組み合わせ文字列
63+
"""
64+
# 全桁処理完了時に結果保存
65+
if index == len(digits):
66+
res.append(path)
67+
return
68+
69+
# 現在の数字に対応する文字で探索
70+
for letter in phone_map[digits[index]]:
71+
backtrack(index + 1, path + letter)
72+
73+
backtrack(0, "")
74+
return res
75+
76+
# ## **動作例**
77+
78+
# ```python
79+
# solution = Solution()
80+
# print(solution.letterCombinations("23"))
81+
# # 出力: ['ad', 'ae', 'af', 'bd', 'be', 'bf', 'cd', 'ce', 'cf']
82+
# ```
83+
84+
# ---
85+
86+
# ## **解説図(探索木)**
87+
88+
# ```
89+
# digits = "23"
90+
91+
# ""
92+
# / | \
93+
# "a" "b" "c"
94+
# / \ / \ / \
95+
# "ad" "ae" "bd" "be" "cd" "ce"
96+
# \ \ \ \ \ \
97+
# "af" "af" "bf" "bf" "cf" "cf"
98+
99+
# 出力: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]
100+
# ```
101+
102+
# ---
103+
104+
# ## **時間計算量の詳細**
105+
106+
# | 項目 | 内容 |
107+
# | -------- | ----------------------- |
108+
# | 1桁の組み合わせ | O(3) or O(4) |
109+
# | 2桁の組み合わせ | O(3×3) = 9 or O(4×3)=12 |
110+
# | 最大4桁 | O(3^N × 4^M) |
111+
112+
# ---
113+
114+
# ## **空間計算量**
115+
116+
# | 項目 | 内容 |
117+
# | ------ | ----------------- |
118+
# | 出力リスト | O(組み合わせ数) |
119+
# | 再帰スタック | O(最大深さ=digitsの長さ) |
120+
121+
# ---
122+
123+
# ## **まとめ**
124+
125+
# * LeetCode提出形式 ✅
126+
# * 型ヒント ✅
127+
# * 計算量解析 ✅
128+
# * 図解による動作説明 ✅
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// ローカル実行や関数のみで使いたい場合は、**グローバル関数で実装**できます。
2+
// LeetCodeの制約を無視して「関数版」のコードを提供します。
3+
4+
// ---
5+
6+
// ## **TypeScript 関数実装(クラスなし)**
7+
8+
// ```typescript
9+
/**
10+
* 与えられた数字文字列から、考えられる全ての文字列の組み合わせを返す
11+
* @param digits - 数字文字列(0 <= digits.length <= 4)
12+
* @returns 可能な文字列組み合わせの配列
13+
*
14+
* 時間計算量: O(3^N × 4^M)
15+
* 空間計算量: O(出力サイズ + 再帰スタック)
16+
*/
17+
function letterCombinations(digits: string): string[] {
18+
if (digits.length === 0) return [];
19+
20+
const phoneMap: Record<string, string> = {
21+
'2': "abc", '3': "def", '4': "ghi",
22+
'5': "jkl", '6': "mno", '7': "pqrs",
23+
'8': "tuv", '9': "wxyz"
24+
};
25+
26+
const result: string[] = [];
27+
28+
function backtrack(index: number, path: string): void {
29+
if (index === digits.length) {
30+
result.push(path);
31+
return;
32+
}
33+
34+
const currentDigit = digits[index];
35+
const letters = phoneMap[currentDigit];
36+
37+
for (const letter of letters) {
38+
backtrack(index + 1, path + letter);
39+
}
40+
}
41+
42+
backtrack(0, "");
43+
return result;
44+
}
45+
// ```
46+
47+
// ---
48+
49+
// ## **使い方(ローカル実行用)**
50+
51+
// ```typescript
52+
// console.log(letterCombinations("23"));
53+
// // 出力例: ["ad","ae","af","bd","be","bf","cd","ce","cf"]
54+
55+
// console.log(letterCombinations(""));
56+
// // 出力: []
57+
58+
// console.log(letterCombinations("2"));
59+
// // 出力: ["a","b","c"]
60+
// ```
61+
62+
// ---
63+
64+
// ## **計算量**
65+
66+
// | 項目 | 内容 |
67+
// | ----- | ------------------------------------------------------ |
68+
// | 時間計算量 | O(3^N × 4^M)(N: '2', '3', ..., '8' の個数, M: '7'と'9'の個数) |
69+
// | 空間計算量 | O(出力サイズ + 再帰スタック) |
70+
71+
// ---
72+
73+
// ## **用途に応じた使い分け**
74+
75+
// | 用途 | 実装パターン |
76+
// | --------------- | --------------------------------- |
77+
// | LeetCode提出 | `class Solution` 必須 |
78+
// | Node.js / 関数テスト | `function letterCombinations` でOK |
79+
80+
// ---
81+
82+
// もし、**Node.jsでの実行コマンド(`ts-node`)や、最適化版実装**も必要でしたら教えてください!
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
以下は **`letterCombinations` 関数の具体的な処理を図解しながら詳しく解説**します。
2+
3+
---
4+
5+
## **問題概要**
6+
7+
### 入力:
8+
9+
数字列 `"23"`
10+
11+
### 電話キー対応:
12+
13+
| 数字 | 対応文字 |
14+
| -- | ------- |
15+
| 2 | a, b, c |
16+
| 3 | d, e, f |
17+
18+
---
19+
20+
## **再帰(バックトラッキング)の動作イメージ**
21+
22+
### 入力:`digits = "23"`
23+
24+
以下のように、**木構造で全探索**しています。
25+
26+
```
27+
""
28+
/ | \
29+
"a" "b" "c"
30+
/ \ / \ / \
31+
"ad" "ae" "bd" "be" "cd" "ce"
32+
| | | | | |
33+
"af" "af" "bf" "bf" "cf" "cf"
34+
```
35+
36+
---
37+
38+
### **具体的な処理フロー**
39+
40+
1. **初期呼び出し**
41+
42+
```typescript
43+
backtrack(0, "")
44+
```
45+
46+
* index: 0(最初の文字)
47+
* path: ""(現在の文字列は空)
48+
49+
---
50+
51+
2. **1段目(digits\[0] = '2')**
52+
53+
```typescript
54+
letters = "abc"
55+
```
56+
57+
* "a" を選択 → 次の桁へ
58+
* "b" を選択 → 次の桁へ
59+
* "c" を選択 → 次の桁へ
60+
61+
---
62+
63+
3. **2段目(digits\[1] = '3')**
64+
65+
```typescript
66+
letters = "def"
67+
```
68+
69+
各パターンごとに以下を繰り返す:
70+
71+
| 文字列 | 動作 |
72+
| --------- | ------- |
73+
| "a" + "d" | "ad" 完成 |
74+
| "a" + "e" | "ae" 完成 |
75+
| "a" + "f" | "af" 完成 |
76+
| "b" + "d" | "bd" 完成 |
77+
| "b" + "e" | "be" 完成 |
78+
| "b" + "f" | "bf" 完成 |
79+
| "c" + "d" | "cd" 完成 |
80+
| "c" + "e" | "ce" 完成 |
81+
| "c" + "f" | "cf" 完成 |
82+
83+
---
84+
85+
## **全体の組み合わせ図**
86+
87+
```
88+
2: [a, b, c]
89+
3: [d, e, f]
90+
91+
全組み合わせ:
92+
[a] -> [d, e, f] => "ad", "ae", "af"
93+
[b] -> [d, e, f] => "bd", "be", "bf"
94+
[c] -> [d, e, f] => "cd", "ce", "cf"
95+
```
96+
97+
---
98+
99+
## **コード処理ごとの解説**
100+
101+
| 処理 | 内容 | 図解 |
102+
| ------------------------- | ------------ | ------------------------- |
103+
| `backtrack(0, "")` | 探索開始 | `""` |
104+
| `path + letter` | 部分文字列を構築 | 例:"a"、"b"、"c" |
105+
| `index === digits.length` | 終端判定、組み合わせ確定 | "ad"など |
106+
| `result.push(path)` | 結果保存 | `["ad", "ae", "af", ...]` |
107+
108+
---
109+
110+
## **時間・空間計算量**
111+
112+
| 項目 | 内容 |
113+
| ----- | -------------------------------------------------------------- |
114+
| 時間計算量 | O(3^N × 4^M) <br> N:3文字のキー(2,3,4,5,6,8)個数 <br> M:4文字のキー(7,9)個数 |
115+
| 空間計算量 | O(組み合わせ数 + 再帰スタック) |
116+
117+
---
118+
119+
## **まとめ図(最終結果)**
120+
121+
```
122+
Input: "23"
123+
124+
出力:
125+
["ad", "ae", "af",
126+
"bd", "be", "bf",
127+
"cd", "ce", "cf"]
128+
```
129+
130+
---
131+
132+
## **視覚化まとめ**
133+
134+
* **木構造探索(バックトラッキング)**
135+
* **全探索(DFS)で全組み合わせ列挙**
136+
* **3文字×3文字 → 9パターン出力**

0 commit comments

Comments
 (0)