-
Notifications
You must be signed in to change notification settings - Fork 0
49 group anagrams #13
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,40 @@ | ||
| # 49. Group Anagrams | ||
|
|
||
| <https://leetcode.com/problems/group-anagrams/description/> | ||
|
|
||
| ## step1(まず通す) | ||
|
|
||
| 「文字を入れ替えて同じになる」という同値関係に基づいて同値類に分割するという話。 | ||
| ただし、毎回二つの文字列が同値関係を満たすか?とチェックするのは大変なので、一つの文字列を見た時に「それがどの同値類に属するか?」を判定できると嬉しい。 | ||
|
|
||
| そのためには、それぞれの同値類に「名前」をつけたい | ||
|
|
||
| - 文字列じゃなくても、Counter.count() (に相当する操作)をした結果とかでもいいが、実用上は hashable であってほしい | ||
| - 代表元の取り方にルールを設けてそれをその同値類の「名前」にすると文字列で名前がついて良い | ||
| - 例えばソートする | ||
|
|
||
| 制約上、各文字列の長さは 100 以下と短いのでソートすれば良さそう | ||
|
|
||
| カウントする方が線形時間で時間計算量のオーダーとしては小さくなるが、 | ||
|
|
||
| - アルファベット小文字は 26 個しかないので長さ26のタプルを使うこともできるが、コードのわかりやすさが犠牲になりそう | ||
| - `frozenset` のように hashable な dict はないのかということが気になるが、調べたら 3.15 から `frozendict` が組み込み型に追加されるらしい。 | ||
| - <https://docs.python.org/ja/3.15/library/stdtypes.html#frozendict> | ||
| - 現在の安定版の最新は 3.14 なのでまだ一般的に使える感じではなさそう | ||
|
|
||
|
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. アルゴリズムの実行時間を見積もることをおすすめします。
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. ありがとうございます。 今回の場合は配列長が最大 10 ^ 4 , 文字列一つ一つの長さが最大 100 なので、 (テストケースが最大の大きさのものがないとかかもしれないので最終的には実際に計らないとわからないのだろうと思いつつ、見積もりが雑とか計算間違ってるかもしれません。) 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. 10^4 や 100 * log 100がどこからきているか書いておくと良いと思います
はい、テストケースでだいぶ変わると思います。ゆくゆくはleetcodeにたよらず、自分でテストケースを書く癖をつけていくとよいかと思います。(癖というより、自分でテストケースを書きたくなる気持ちを養うといったほうがいいかもしれないです)
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.
省略しすぎました。サボらず伝わる書き方を心がけます。
ありがとうございます。まずは想定できるケースのバリエーションを増やす意識で取り組みます。 |
||
| ## step2(整形&他の人のコードを読む) | ||
|
|
||
| `sorted_string` は `sorted_` とかでもいいかも。今回は基本 string の話しかしてなくて string であることは明らかなので。 | ||
|
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. 関数名と紛らわしくなるため、同じ(もしくはほとんど似た)名前を変数名に使うのは避けたほうが良いと思います。元の sorted_string の方が意図が分かりやすいと感じました。
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. そうですね、書いているうちにそう思うようになりました。 |
||
|
|
||
| あとは他の人の書き方を見る。 | ||
|
|
||
| - <https://github.com/naoto-iwase/leetcode/pull/12#discussion_r2415269272> | ||
| - ちゃんと把握してなかったが、str だけじゃなくて string も予約語(に近い、モジュールがある)らしい | ||
| - そもそも他言語でも string は予約語なことが多いと思うので避けた方がいい | ||
| - スコープも短いし `s` くらいでもいいかも、というコメントもあった | ||
| - counter タプルに詰める時、文字コードを使う発想はなかった | ||
| - <https://github.com/shintaro1993/arai60/pull/16/changes> | ||
| - 文字ごとの出現数を数える場合は、英小文字じゃなかったらスキップしとかないとおかしくなる | ||
| - ord とか all とか、知らない組み込み関数がいろいろある | ||
|
|
||
| ## step3(10分以内にさっとかける * 3回) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| class Solution: | ||
| def groupAnagrams(self, strs: list[str]) -> list[list[str]]: | ||
| sorted_to_originals: dict[str, list[str]] = {} | ||
| for string in 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.
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. ありがとうございます。 今考えてみると割り切って word は良い名前な気がしてます。 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.
おっしゃる通り、場合によってはword以外の変数名も考えたほうが良いかもしれないですね。 |
||
| sorted_string = str(sorted(string)) | ||
| if sorted_string in sorted_to_originals: | ||
|
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. 6~10行目はdefalutdictを使うこともできます。 from collections import defaultdict
class Solution:
def groupAnagrams(self, strs: list[str]) -> list[list[str]]:
sorted_to_originals = defaultdict(list)
for string in strs:
sorted_string = str(sorted(string))
sorted_to_originals[sorted_string].append(string)
return list(sorted_to_originals.values())
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. ありがとうございます。 defaultdict をあまり認識していなかったので調べておきます。 |
||
| sorted_to_originals[sorted_string].append(string) | ||
| else: | ||
| sorted_to_originals[sorted_string] = [string] | ||
|
|
||
| return list(sorted_to_originals.values()) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| class Solution: | ||
| def groupAnagrams(self, strs: list[str]) -> list[list[str]]: | ||
| grouped_anagrams: dict[str, list[str]] = {} | ||
| for s in strs: | ||
| sorted_s = str(sorted(s)) | ||
| if sorted_s in grouped_anagrams: | ||
| grouped_anagrams[sorted_s].append(s) | ||
| else: | ||
| grouped_anagrams[sorted_s] = [s] | ||
|
|
||
| return list(grouped_anagrams.values()) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| class Solution: | ||
| def groupAnagrams(self, strs: list[str]) -> list[list[str]]: | ||
| grouped_anagrams: dict[str, list[str]] = {} | ||
| for s in strs: | ||
| sorted_s = str(sorted(s)) | ||
| if sorted_s in grouped_anagrams: | ||
| grouped_anagrams[sorted_s].append(s) | ||
| else: | ||
| grouped_anagrams[sorted_s] = [s] | ||
| return list(grouped_anagrams.values()) |
Uh oh!
There was an error while loading. Please reload this page.
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.
正確な説明ですが、同値関係や同値類あたりはソフトウェアエンジニアの世界では普段から使う言葉とは感じないので、避けてもいいかもしれません。
「並べ替えると同じになる文字列を同じグループにまとめる」でいいかと思います。
Uh oh!
There was an error while loading. Please reload this page.
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.
そうですね、自分の思考過程をメモ書き程度に書いたつもりでしたが他の方も見るので言葉遣いは周りに合わせたほうが良さそうです。
↓のような説明だとだいぶマシでしょうか。
skypenguins/coding-practice#32 (comment)
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.
いいと思います。
効率の良さを議論するときは、それによって何が嬉しいのかも意識すると良いと思います。想像の域は出ませんが、例えば、前者の方法で実装されたプログラムが既に動いているとして、後者へ変更する価値があるのかを考えてみるとよいと思います。実行時間を見積もる意味も、そういった判断材料を得るところにあると私は思っています。
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.
まさにそうですね、困ってない(し、これからも困ることはなさそう)なら触らないというのは実務では多いですよね。
実践的なアドバイスありがとうございます!