Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions Python3/49. Group Anagrams.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
## Step 1. Initial Solution

- 文字列をそれぞれソートして一致するものを同じグループに入れる
- 辞書型にしてソートされた文字列と結果のインデックスを紐づけておく
- 文字列に対してsortedを使うとどうなるのか
- sortされた文字列が返ってくる前提でやるとエラー
- リストが返ってきていそうなのでstr()で文字列化

```python
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
result: List[List[int]] = []
sorted_str_idx: Dict[str, int] = {}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorted_str_idx という変数名ですと、ソートされた文字列のインデックスという意味に感じられます。また、英単語から文字を削って変数名にすると、やや認知負荷が上がるように思います。 to を入れて、 sorted_to_str_index はいかがでしょうか?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

少し冗長かなと感じてしまいましたが、その方が分かりやすいですね

for str_i in strs:
Copy link
Copy Markdown

@chanseok-lim chanseok-lim May 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strが使えないために咄嗟にiをつけたものだと思いますが、str_iという変数名はあまり見ない気がします。iが意味を持ててないですし、ともすればindexであるという誤解を招くこともあろうかと思います。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

素直にstringとかで良かったんでしょうか?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sでいいと思います。

sorted_str = str(sorted(str_i))
if sorted_str not in sorted_str_idx:
sorted_str_idx[sorted_str] = len(result)
result.append([str_i])
continue
idx = sorted_str_idx[sorted_str]
result[idx].append(str_i)
return result
```

### Complexity Analysis

- 時間計算量:O(n * m log m)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

今回の範囲だとこの処理は何秒くらいかかりそうでしょうか?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

文字列長は≤100で文字列数は≤10000なので、10^6ぐらいのオーダーになりそうです。10^-2秒のオーダーで計算できるのかなと思います。
確かにこういった時間の正確な見積もりはあまりイメージを持って出来ていなかったので参考になります。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

文字列のソートは O(m log m) ですので、 100 * 7 くらいになると思います。
また、 Python はかなり遅い言語で、 1 秒間に 100~1000 万ステップくらいしか実行できないと思います。
https://github.com/niklas-heer/speed-comparison
https://benchmarksgame-team.pages.debian.net/benchmarksgame/box-plot-summary-charts.html

諸々を考えると 1 秒くらいになると思います。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

試してみるといいかもしれませんね。
文字列のソートはネイティブコードで走るので、もう少し速いかもしれません。

- 文字列の数×文字列のソート
- 文字列化の実装の中身は詳しく分かっていない
- __str__メソッドの定義による
- print()で出力されるものと同じ
- 空間計算量:O(n)
- 文字列の数

## Step 2. Alternatives

- Dictのvaluesに直接文字列を入れて、values()を出力する方法もある
- コードとしてもこちらの方が最小限で書けている
- ただ、この状態だとkeyが`"['a', 'e', 't']"` のようになってしまい少し違和感がある
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

はい。私もそのkeyには違和感があります。


```python
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
sorted_to_group: Dict[str, List[str]] = defaultdict(list)
for anagram in strs:
sorted_to_group[str(sorted(anagram))].append(anagram)
return list(sorted_to_group.values())
```

- str()を呼び出すよりも””.join()を使う方が良い
- strでは各要素のあとにカンマを入れる処理が必要なので遅い
- keyも`"aet"` のようになって分かりやすい

```python
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
sorted_to_group: Dict[str, List[str]] = defaultdict(list)
for anagram in strs:
sorted_to_group["".join(sorted(anagram))].append(anagram)
return list(sorted_to_group.values())
```

- あまり使う意味はないかもしれないが、tupleにするという手もあると思った
- keyは`["a","e","t"]` のようになる

```python
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
sorted_to_group: Dict[Tuple[str], List[str]] = defaultdict(list)
for anagram in strs:
sorted_in_tuple: Tuple[str] = tuple(sorted(anagram))
sorted_to_group[sorted_in_tuple].append(anagram)
return list(sorted_to_group.values())

```

- 異常な入力に関して、空リストが来たら空リストを返す
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

空リスト以外の異常な入力にはどのようなものがあり得て、どう対処すればよいでしょうか?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

リストじゃないとか、リストに文字列以外のものが入っているとかですね。
リストでないiterableはfor文回せてしまうのでその前でエラー出したいですね。
リストに文字列以外が来た場合はこれもsort出来てしまわないようにエラーだしたいですね。
具体的にこういった場合を考慮したコードも書いていこうと思います。ご指摘ありがとうございます

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

リストじゃないとか、リストに文字列以外のものが入っているとかですね。

そうですね。

リストでないiterableはfor文回せてしまうのでその前でエラー出したいですね。

組み込みのisinstanceを使うのがいいと思います。
(https://docs.python.org/ja/3.13/library/functions.html#isinstance)[https://docs.python.org/ja/3.13/library/functions.html#isinstance]

リストに文字列以外が来た場合はこれもsort出来てしまわないようにエラーだしたい

そうですね。あとはlowercase / uppercaeはどう扱うとかも考えられそうです。
例えば"aa"と"aA"みたいなケースです。

- 空文字列が来ていないのに空文字列を返すのもおかしいのでこれで良さそう
- tupleとfrozensetはimmutableだが、要素がhashableな場合だけhashableとのこと
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

こういうのは、公式サイトへのリンクを貼りましょう。
https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0#heading=h.9xjrags8izok

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

すみません、承知しました。

- 脳死で辞書型のkeyに使えると思わないようにする

## Step 3. Final Solution

- 命名はこの辺りが分かりやすそうに感じた

```python
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
grouped_anagrams: Dict[str, List[str]] = defaultdict(list)
for anagram in strs:
sorted_anagram = "".join(sorted(anagram))
grouped_anagrams[sorted_anagram].append(anagram)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1行に3回もanagramという文字が並んでいて辛いです。もう少し視認性を良くできませんか?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

迷彩みたいな話ですね。変数名は長ければよいというものではないです。
https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0#heading=h.fcs3httrll4l

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

気を付けたいと思います。anagramをわざわざ入れなくても良いところでは避けようと思います

return list(grouped_anagrams.values())

```