-
Notifications
You must be signed in to change notification settings - Fork 0
LeetCode 17. Letter Combinations of a Phone Number #60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
huyfififi
wants to merge
9
commits into
main
Choose a base branch
from
leetcode-17-letter-combinations-of-a-phone-number
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
22810a3
LeetCode 17: Add step 1 solution
huyfififi a734acb
LeetCode 17: Note my realization on backtracking
huyfififi 5ebacc6
LeetCode 17: Add step 1 solution with backtracking
huyfififi 65c3686
LeetCode 17: Add step 2 solution
huyfififi 6f32884
LeetCode 17: Fix type hints
huyfififi 5f6d95a
LeetCode 17: Add step 3 solution
huyfififi cf05de2
LeetCode 17: Note my research on std::string bytes size
huyfififi f90d0aa
LeetCode 17: Tweak wording
huyfififi 11cc1cc
LeetCode 17: Add step 4 solution which reduces unnecessary copies
huyfififi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| # Step 1 | ||
|
|
||
| 各桁の間に関係性がなさそうなので、素直に全通り生成していく問題かなと思った。 | ||
|
|
||
| > `1 <= digits.length <= 4` | ||
|
|
||
| で、1 つの digit が最大 4 つの文字を取りうるので、`4 ^ 4 = 2 ^ 8 = 2 ^ 10 / 2 ^ 2 = 1024 / 4 = 256` 通りの結果を最悪作ることになる。 | ||
| 木を考えると、一段目が 4、次が16、64、256 と続いて、全部で 340 のノードが存在し得るが、これは C++ が 1秒間で 10^8 - 10^9 回の処理を行えることを考えると、実行時間は問題にならないように思う。 | ||
| 最終的に長さ 4 のstring を 256 個持つことになるが、char が 1 byte、4 つで 4 bytes、std::stringのサイズに詳しくないので一旦そこは無視しても、256 * 4 bytes = 1 KB。特に問題になるような大きさではないだろう。 | ||
| (追記: std::string は、この文字列の長さなら私の環境で 1 つ 24 bytes, なので最終結果の保持に 256 * 24 bytes = 6KB) | ||
|
|
||
| 時間計算量: O(n * 4 ^ n) (string construction + generate all possible combinations) | ||
| 空間計算量: O(n * 4 ^ n) (the result holds 4 ^ n strings with length of n) | ||
|
|
||
| テストケースは無事にパスしたのだが、LeetCode 上の実行時間・補助空間使用量の順位が最下位に近い。constant 倍くらいしか違わなそうだが、私の解法よりも効率の良い解法がありそうだ。~Step 2 で見てみることにする。~ と思ったのだが、これ backtracking で行けるのでは? C++のsyntaxを思い出すことにいっぱいいっぱいになってしまっていたが、Pythonだったらnested functions を書くのが楽なので helper 関数を用意して backtracking という方法がすぐに思いついたのにな、悔しい。 | ||
|
|
||
| -> `step1_backtracking.py` | ||
|
|
||
| 途中結果をもつ `list[str]` を使い回すことで、`step1.cpp`のように途中結果を全て保持しなくて済んでいる。 | ||
|
|
||
| ところで、digit -> characters の hashmap は書き下した方がわかりやすいと思う。これ以上の工夫は思いつかず、書き下している方が将来キー配置の変更があったとしても対応しやすく、差分が理解しやすいように思う。 | ||
|
|
||
| # Step 2 | ||
|
|
||
| ざっと std::string のサイズについてGoogleした限りこれだ!と思うリファレンスはなかったが、色々見た上での私の理解は以下の通り。 | ||
| 環境によって std::string のオブジェクトサイズは 24\~32 bytes。(私の手元の環境だと24 bytes だった) | ||
| 短い文字列は Short String Optimization (SSO) によりオブジェクト内部に格納されるが、 | ||
| 一定の長さを超えると SSO が適用されなくなり、ヒープ領域に文字列用のメモリが別途確保される。 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| #include <map> | ||
| #include <utility> | ||
| #include <stack> | ||
| #include <string> | ||
| #include <vector> | ||
|
|
||
| class Solution { | ||
| public: | ||
| std::vector<std::string> letterCombinations(std::string digits) { | ||
| std::map<char, std::vector<char>> digit_to_characters = { | ||
| {'2', {'a', 'b', 'c'}}, | ||
| {'3', {'d', 'e', 'f'}}, | ||
| {'4', {'g', 'h', 'i'}}, | ||
| {'5', {'j', 'k', 'l'}}, | ||
| {'6', {'m', 'n', 'o'}}, | ||
| {'7', {'p', 'q', 'r', 's'}}, | ||
| {'8', {'t', 'u', 'v'}}, | ||
| {'9', {'w', 'x', 'y', 'z'}}, | ||
| }; | ||
|
|
||
| std::vector<std::string> possible_combinations; | ||
| std::stack<std::pair<int, std::vector<char>>> combinations_in_progress; | ||
| combinations_in_progress.push({0, {}}); | ||
| while (!combinations_in_progress.empty()) { | ||
| auto [digit_index, combination_in_progress] = combinations_in_progress.top(); | ||
| combinations_in_progress.pop(); | ||
|
|
||
| if (digit_index == digits.size()) { | ||
| std::string possible_combination( | ||
| combination_in_progress.begin(), | ||
| combination_in_progress.end() | ||
| ); | ||
| possible_combinations.push_back(possible_combination); | ||
| continue; | ||
| } | ||
|
|
||
| for (char possible_character : digit_to_characters[digits[digit_index]]) { | ||
| std::vector<char> next_combination(combination_in_progress); | ||
| next_combination.push_back(possible_character); | ||
| combinations_in_progress.push({digit_index + 1, next_combination}); | ||
| } | ||
| } | ||
|
|
||
| return possible_combinations; | ||
| } | ||
| }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| class Solution: | ||
| def letterCombinations(self, digits: str) -> list[str]: | ||
| digit_to_characters: dict[str, list[str]] = { | ||
| "2": ["a", "b", "c"], | ||
| "3": ["d", "e", "f"], | ||
| "4": ["g", "h", "i"], | ||
| "5": ["j", "k", "l"], | ||
| "6": ["m", "n", "o"], | ||
| "7": ["p", "q", "r", "s"], | ||
| "8": ["t", "u", "v"], | ||
| "9": ["w", "x", "y", "z"], | ||
| } | ||
|
|
||
| all_combinations = [] | ||
|
|
||
| def generate_combinations(digit_index: int, combination: list[str]) -> None: | ||
| if digit_index == len(digits): | ||
| all_combinations.append("".join(combination)) | ||
| return | ||
|
|
||
| for character in digit_to_characters[digits[digit_index]]: | ||
| combination.append(character) | ||
| generate_combinations(digit_index + 1, combination) | ||
| combination.pop() | ||
|
|
||
| generate_combinations(0, []) | ||
| return all_combinations |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| class Solution: | ||
| def letterCombinations(self, digits: str) -> list[str]: | ||
| digit_to_characters: dict[str, list[str]] = { | ||
| "2": ["a", "b", "c"], | ||
| "3": ["d", "e", "f"], | ||
| "4": ["g", "h", "i"], | ||
| "5": ["j", "k", "l"], | ||
| "6": ["m", "n", "o"], | ||
| "7": ["p", "q", "r", "s"], | ||
| "8": ["t", "u", "v"], | ||
| "9": ["w", "x", "y", "z"], | ||
| } | ||
|
|
||
| generated = [] | ||
|
|
||
| def generate_combinations(digit_index: int, generating: list[str]) -> None: | ||
| if digit_index == len(digits): | ||
| generated.append("".join(generating)) | ||
| return | ||
|
|
||
| for character in digit_to_characters[digits[digit_index]]: | ||
| generating.append(character) | ||
| generate_combinations(digit_index + 1, generating) | ||
| generating.pop() | ||
|
|
||
| generate_combinations(0, []) | ||
| return generated |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| class Solution: | ||
| def letterCombinations(self, digits: str) -> list[str]: | ||
| digit_to_characters = { | ||
| "2": "abc", | ||
| "3": "def", | ||
| "4": "ghi", | ||
| "5": "jkl", | ||
| "6": "mno", | ||
| "7": "pqrs", | ||
| "8": "tuv", | ||
| "9": "wxyz", | ||
| } | ||
|
|
||
| def generate_combinations( | ||
| digit_index: int, generating: list[str], generated: list[str] | ||
| ) -> None: | ||
| if digit_index == len(digits): | ||
| generated.append("".join(generating)) | ||
| return | ||
|
|
||
| for ch in digit_to_characters[digits[digit_index]]: | ||
| generating.append(ch) | ||
| generate_combinations(digit_index + 1, generating, generated) | ||
| generating.pop() | ||
|
|
||
| generated = [] | ||
| generate_combinations(0, [], generated) | ||
| return generated |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| #include <map> | ||
| #include <utility> | ||
| #include <stack> | ||
| #include <string> | ||
| #include <vector> | ||
|
|
||
| class Solution { | ||
| public: | ||
| std::vector<std::string> letterCombinations(std::string digits) { | ||
| if (digits.empty()) { return {}; } | ||
|
|
||
| std::map<char, std::vector<char>> digit_to_characters = { | ||
| {'2', {'a', 'b', 'c'}}, | ||
| {'3', {'d', 'e', 'f'}}, | ||
| {'4', {'g', 'h', 'i'}}, | ||
| {'5', {'j', 'k', 'l'}}, | ||
| {'6', {'m', 'n', 'o'}}, | ||
| {'7', {'p', 'q', 'r', 's'}}, | ||
| {'8', {'t', 'u', 'v'}}, | ||
| {'9', {'w', 'x', 'y', 'z'}}, | ||
| }; | ||
|
|
||
| std::vector<std::string> possible_combinations; | ||
| std::stack<std::pair<int, std::vector<char>>> combinations_in_progress; | ||
| combinations_in_progress.push({0, {}}); | ||
| while (!combinations_in_progress.empty()) { | ||
| auto top = std::move(combinations_in_progress.top()); | ||
| combinations_in_progress.pop(); | ||
| auto [digit_index, combination_in_progress] = std::move(top); | ||
|
|
||
| if (digit_index == digits.size()) { | ||
| possible_combinations.emplace_back( | ||
| combination_in_progress.begin(), combination_in_progress.end() | ||
| ); | ||
| continue; | ||
| } | ||
|
|
||
| for (char possible_character : digit_to_characters[digits[digit_index]]) { | ||
| std::vector<char> next_combination(combination_in_progress); | ||
| next_combination.push_back(possible_character); | ||
| combinations_in_progress.emplace(digit_index + 1, std::move(next_combination)); | ||
| } | ||
| } | ||
|
|
||
| return possible_combinations; | ||
| } | ||
| }; |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
不要なオブジェクトのコピーが走っているところがいくつかありそうです。こことか。
std::moveした方が良さそうです。There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ありがとうございます。自分の中で欠けていた視点だったので、参考になりました。