-
Notifications
You must be signed in to change notification settings - Fork 0
49. Group Anagrams #12
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,174 @@ | ||||||
| ## 問題 | ||||||
|
|
||||||
| [Group Anagrams - LeetCode](https://leetcode.com/problems/group-anagrams/description/) | ||||||
|
|
||||||
| - 入力 | ||||||
| - `strs`: 文字列配列 | ||||||
| - 長さは1以上10^4以下 | ||||||
| - 要素の文字列の長さは0以上100以下 | ||||||
| - 要素の文字列は英語小文字 | ||||||
| - 出力 | ||||||
| - `strs` の要素をアナグラムのグループごとにまとめた配列 | ||||||
| - 順序は任意 | ||||||
|
|
||||||
| ## 解法 | ||||||
|
|
||||||
| ### 文字列要素をソートして map のキーとし、スライスに追加していく | ||||||
|
|
||||||
| - 時間計算量 | ||||||
| - ソートの時間計算量は O(N log N): N は文字列要素の長さで最大値10^2 | ||||||
| - これを各要素ごとに行う O(M): M は文字列要素の数で最大10^4 | ||||||
| - ざっくり O(MN log N) | ||||||
| - 空間計算量 | ||||||
| - 文字列のコピーが必要: O(MN) | ||||||
| - map のキーでO(M)、値でO(MN) | ||||||
| - ざっくりO(MN) | ||||||
| - 最大で10^4*10^2 = 1MB程度 | ||||||
| - map でざっくり2倍の2MBくらい | ||||||
|
|
||||||
| ### 文字列の登場回数を map のキーとし、スライスに追加していく | ||||||
|
|
||||||
| - 英語小文字という問題設定上 `byte` として処理する | ||||||
| - `rune` を使う必要がある場合は使えない | ||||||
| - ソートのほうが汎用的 | ||||||
| - 時間計算量 | ||||||
| - 各文字列について各文字ごとにカウント | ||||||
| - O(MN) | ||||||
| - 空間計算量 | ||||||
| - 文字要素の長さは最大100だから各文字の登場回数は `byte` に収まる | ||||||
| - 長さ26の固定長配列を最大10^4個用意する | ||||||
| - 260KB | ||||||
| - go の map はざっくり2倍で520KB | ||||||
| - 大丈夫そう | ||||||
|
|
||||||
| ## Step1 | ||||||
|
|
||||||
| ### ソート版 | ||||||
|
|
||||||
| ```go | ||||||
| func groupAnagrams(strs []string) [][]string { | ||||||
| groups := make(map[string][]string, len(strs)) | ||||||
|
|
||||||
| for _, str := range strs { | ||||||
| strBytes := []byte(str) | ||||||
| slices.Sort(strBytes) | ||||||
| groups[string(strBytes)] = append(groups[string(strBytes)], str) | ||||||
| } | ||||||
|
|
||||||
| anagramGroups := make([][]string, 0, len(groups)) | ||||||
| for _, anagrams := range groups { | ||||||
| anagramGroups = append(anagramGroups, anagrams) | ||||||
| } | ||||||
|
|
||||||
| return anagramGroups | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| ### 配列版 | ||||||
|
|
||||||
| ```go | ||||||
| func groupAnagrams(strs []string) [][]string { | ||||||
| groups := make(map[[26]byte][]string, len(strs)) | ||||||
|
|
||||||
| for _, str := range strs { | ||||||
| var counts [26]byte | ||||||
| for i := 0; i < len(str); i++ { | ||||||
| counts[str[i]-'a']++ | ||||||
| } | ||||||
|
Comment on lines
+73
to
+77
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. leetcode 上ではアルファベットが入力として与えられているため問題になりませんが、アルファベット以外の記号がきた場合には、インデックスがマイナスになる場合が考えられます。これを避けるために、アルファベット以外が来たときは、エラーを投げる、continue する、などの対処法を考えても良さそうです。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ありがとうございます。 そうですね、実務で想定外の入力を考慮する場合はエラーを返すようにします。 OuterLoop:
for _, str := range strs {
var counts [26]byte
for i := 0; i < len(str); i++ {
if str[i] < 'a' || 'z' < str[i] {
continue OuterLoop
}
counts[str[i]-'a']++
}
groups[counts] = append(groups[counts], str)
} |
||||||
|
|
||||||
| groups[counts] = append(groups[counts], str) | ||||||
| } | ||||||
|
|
||||||
| anagramGroups := make([][]string, len(groups)) | ||||||
| i := 0 | ||||||
| for _, anagrams := range groups { | ||||||
| anagramGroups[i] = anagrams | ||||||
| i++ | ||||||
| } | ||||||
|
|
||||||
| return anagramGroups | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| ## Step2 | ||||||
|
|
||||||
| ### ソート版 | ||||||
|
|
||||||
| ```go | ||||||
| func groupAnagrams(strs []string) [][]string { | ||||||
| keyToAnagramGroup := make(map[string][]string, len(strs)) | ||||||
|
|
||||||
| for _, str := range strs { | ||||||
| key := generateKey(str) | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. この関数の括りだし、可読性が上がっていいですね。
Suggested change
これだと「ソートした文字列をキーにするよ」が分かりやすいのではないでしょうか。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ありがとうございます。 |
||||||
| keyToAnagramGroup[key] = append(keyToAnagramGroup[key], str) | ||||||
| } | ||||||
|
|
||||||
| return slices.Collect(maps.Values(keyToAnagramGroup)) | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. こんな便利なメソッドを用意してくれてるんですね。 Goって基本的には「愚直に書け」って言ってくるので感心しました。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ここ最近で追加されました。 |
||||||
| } | ||||||
|
|
||||||
| func generateKey(s string) string { | ||||||
| sBytes := []byte(s) | ||||||
| slices.Sort(sBytes) | ||||||
| return string(sBytes) | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| ### 配列版 | ||||||
|
|
||||||
| ```go | ||||||
| func groupAnagrams(strs []string) [][]string { | ||||||
| keyToAnagramGroup := make(map[[26]byte][]string, len(strs)) | ||||||
|
|
||||||
| for _, str := range strs { | ||||||
| key := generateKey(str) | ||||||
| keyToAnagramGroup[key] = append(keyToAnagramGroup[key], str) | ||||||
| } | ||||||
|
|
||||||
| return slices.Collect(maps.Values(keyToAnagramGroup)) | ||||||
| } | ||||||
|
|
||||||
| func generateKey(s string) [26]byte { | ||||||
| var counts [26]byte | ||||||
| for _, b := range []byte(s) { | ||||||
| counts[b-'a']++ | ||||||
| } | ||||||
|
|
||||||
| return counts | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| - [slices package - slices - Go Packages](https://pkg.go.dev/slices#Collect) | ||||||
| - [maps package - maps - Go Packages](https://pkg.go.dev/maps#Values) | ||||||
| - 英語小文字を前提としているコード | ||||||
|
|
||||||
| ### レビューを依頼する方のPR | ||||||
|
|
||||||
| - [49. Group Anagrams by mamo3gr · Pull Request #12 · mamo3gr/arai60](https://github.com/mamo3gr/arai60/pull/12) | ||||||
| - 単語の数を `N` 、単語の長さを `W` とおくとわかりやすい | ||||||
| - [49_group_anagrams by Hiroto-Iizuka · Pull Request #12 · Hiroto-Iizuka/coding_practice](https://github.com/Hiroto-Iizuka/coding_practice/pull/12) | ||||||
| - [49. Group Anagrams by TakayaShirai · Pull Request #12 · TakayaShirai/leetcode_practice](https://github.com/TakayaShirai/leetcode_practice/pull/12) | ||||||
| - Dart | ||||||
| - [49. Group Anagram by dxxsxsxkx · Pull Request #12 · dxxsxsxkx/leetcode](https://github.com/dxxsxsxkx/leetcode/pull/12) | ||||||
| - ハッシュを使われていた | ||||||
| - [49. Group Anagrams by aki235 · Pull Request #12 · aki235/Arai60](https://github.com/aki235/Arai60/pull/12) | ||||||
|
|
||||||
| ## Step3 | ||||||
|
|
||||||
| ```go | ||||||
| func groupAnagrams(strs []string) [][]string { | ||||||
| keyToAnagramGroup := make(map[string][]string, len(strs)) | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [nits]
Suggested change
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. そうですね、「伝わるか?」ということを考えてなるべく短い名前を採用するようにします |
||||||
|
|
||||||
| for _, s := range strs { | ||||||
| key := generateKey(s) | ||||||
| keyToAnagramGroup[key] = append(keyToAnagramGroup[key], s) | ||||||
| } | ||||||
|
|
||||||
| return slices.Collect(maps.Values(keyToAnagramGroup)) | ||||||
| } | ||||||
|
|
||||||
| func generateKey(s string) string { | ||||||
| sBytes := []byte(s) | ||||||
| slices.Sort(sBytes) | ||||||
| return string(sBytes) | ||||||
| } | ||||||
| ``` | ||||||
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.
Go に詳しくないため、理解が間違っていたら申し訳ないです。
ここは破壊的な操作に思われるため、もし入力を変更しなくない場合は、非破壊での操作を検討するのも良いのかなと思いました。
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.
レビューありがとうございます。
stringから[]byteへ変換後のSortしていることについてコメントいただいたと考えました。Go の仕様では
stringは変更不可能なリソースとなっています。The Go Programming Language Specification - The Go Programming Language
[]byte(str)で[]byteへ型変換すると、新たにメモリが確保されコピーされます。そのため、上記コードでは入力データは破壊されないようになっています。
The Go Programming Language Specification - The Go Programming Language
改めて公式ドキュメントを確認する機会をいただき、ありがとうございましたmm
今後ともよろしくお願いいたします。