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
100 changes: 100 additions & 0 deletions 49/49.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
## Step1
5分以内に書けなかった。
各単語に対して、{a_i: n_i}, a_i = i番目のアルファベット、n_i = その単語がi番目のアルファベットを含む数というdictを作成して、dict同士を比べれば何とかなるかなと思い始めて頃に5分が経っていた。
よく考えるとこの実装はアルファベット以外のstrはどうするんだという問題がある。
sortして同一であるのがanagramだとは気づかなかったので、解答を見てなるほどと思った。
```python
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
d = defaultdict(list)
for s in strs:
k = ''.join(sorted(s))
d[k].append(s)

return list(d.values())
```
## Step2
レビュー依頼の直近5人についてレビューを読む。
[https://github.com/shintaro1993/arai60/pull/16](https://github.com/shintaro1993/arai60/pull/16)<br>
sortしないで済む場合分けを入れてる。<br><br>

[https://github.com/plushn/SWE-Arai60/pull/12](https://github.com/plushn/SWE-Arai60/pull/12)<br>
小文字以外は弾く。そういえば小文字大文字はどうsortされるんだったか?<br>
問題の設定は全てlowercaseだけど、"Ab"と"ab"は同じとして扱うべきか? それとも弾くべきか?<br><br>

[https://github.com/fuga-98/arai60/pull/13](https://github.com/fuga-98/arai60/pull/13)<br>
```python
my_list = ['a', 'b', 'c']
print(str(my_list))
# 出力: "['a', 'b', 'c']"
```
strにlist入れるとそうなるのか。<br><br>

[https://github.com/ichika0615/arai60/pull/11](https://github.com/ichika0615/arai60/pull/11)<br>
単語をsortした後にordにして、'a'との差分からアルファベットを特定する。<br><br>

[https://github.com/Mahiro-3612/leetcode/pull/2](https://github.com/Mahiro-3612/leetcode/pull/2)<br>
時間計算量も求めること。<br><
Cf. [https://discord.com/channels/1084280443945353267/1307605446538039337/1336992875577081917](https://discord.com/channels/1084280443945353267/1307605446538039337/1336992875577081917)<br>

```python
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
if not isinstance(strs, list):
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Duck Typing, type, isinstanceの違いを見てみてもよいかもしれません
fuga-98/arai60#37 (comment)

Copy link
Copy Markdown
Owner Author

@chanseok-lim chanseok-lim Apr 30, 2025

Choose a reason for hiding this comment

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

@fuga-98
まずはレビューありがとうございます!

動かないコードを乗せるときは明示していただけると助かります。

そうですね。次からそのようにします!
あのコードが動かないのはdictのkeyとしてhashableではないdictを使っているからであるというのが僕の理解です。

ここをこう変えれば動きますね
return tuple(sorted(unicode_to_frequency))

めちゃくちゃ興味深いです!
dictをsortedした時の挙動を初めて知りました。keyのlistが返ってくるんですね。
そしてlistをtuple (immutable; immutableならhashable) に変換してkeyとして与えると。
テストをクリアする面白いアイディアだと思うのですが、この実装ですと"aa"と"a"が同じアナグラムとして扱われるのでは?

Duck Typing, type, isinstanceの違いを見てみてもよいかもしれません

ありがとうございます! 参照します!

Copy link
Copy Markdown

@fuga-98 fuga-98 Apr 30, 2025

Choose a reason for hiding this comment

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

すみません、不正確な情報を書いてしまいました。

raise TypeError(f'Expected: list, Actual: {type(strs)}')

grouped_anagrams = defaultdict(list)
for s in strs:
basis_anagram = ''.join(sorted(s))
grouped_anagrams[basis_anagram].append(s)

return list(grouped_anagrams.values())
```
sortedの計算量はO(nlogn)らしい<br>
[https://stackoverflow.com/questions/14434490/what-is-the-complexity-of-the-sorted-function](https://stackoverflow.com/questions/14434490/what-is-the-complexity-of-the-sorted-function)<br>
すると
計算量:O(mnlogn), m, n = strs.length, max(strs[i].length)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

正確なことは分かりませんが、26文字のソートは計算量が少なそうです。
詳しい方教えてください。

Copy link
Copy Markdown
Owner Author

@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.

26文字のソートは計算量が少なそうです。

26文字のソートというのは何を指しますでしょうか?
アルファベットのソートは特別に高速でありそうだということでしょうか?

時間計算量: 2*10^6 / 10^6 = 2 s

## Step3
10分に3回できた。

## 感想
最初の方針と前回を思い出しながらコードを実装して動かすと`TypeError: unhashable type: 'collections.defaultdict'`となった。<br>
dictをkeyするのは流石に無茶かと思っていたが、やはり無茶だった。<br>
hashableについて2つの記事、
[hashableについてのqiita記事](https://qiita.com/yoichi22/items/ebf6ab3c6de26ddcc09a#__hash__-%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%81%8C%E3%81%82%E3%82%8C%E3%81%B0-hashable)と
[公式ドキュメントの__hash__の項目](https://docs.python.org/ja/3/reference/datamodel.html#object.__hash__)、
を読む。面白い。考えたこともなかった。
```python
class Solution:
def count_unicodes_in_word(self, word: str) -> dict:
# type check
if type(word) is not str:
raise TypeError(f'expected: str, actual: {type(word)}')

unicode_to_frequency = defaultdict(int)
for s in word:
Copy link
Copy Markdown

@fuga-98 fuga-98 Apr 30, 2025

Choose a reason for hiding this comment

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

文字はsではなくcharacterのcが普通ですかね

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.

sが意味が入ってこないというのはご指摘の通りですね。
ただwordという変数名をつけたなら、characterよりletterかなとも思います。

一文字変数を使ってしまいましたがfor letter in wordとするのがいい気がしてきました。

unicode_to_frequency[ord(s)] += 1

return unicode_to_frequency
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ここをこう変えれば動きますね
return tuple(sorted(unicode_to_frequency))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

sortの仕様を間違えて覚えていました。正しくはこうですね。

sorted_unicode_to_frequency = sorted([(u, f) for u, f in unicode_to_frequency.items()])
return tuple(sorted_unicode_to_frequency)
sorted_unicode_to_frequency = sorted(unicode_to_frequency.items(), key=lambda x:x[0])
return tuple(sorted_unicode_to_frequency)

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.

@fuga-98
追加のコメントありがとうございます!
これなら問題なさそうですね。

sorted_unicode_to_frequency = sorted([(u, f) for u, f in unicode_to_frequency.items()])

僕の感覚として、2次元のものをそのまま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.

二次元のものをソートするものはわりと見る気がします。
ソートを使わないなら二次元のfrozensetにしても良いかもしれません。

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 というよりは dict の仕様ですね。iterable として解釈すると key を舐めます。
https://peps.python.org/pep-0234/#dictionary-iterators

frozendict もありますね。

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.

@oda
まずはレビューありがとうございます!

そうか。そこには思い至りませんでした!

sorted(iterable, /, *, key=None, reverse=False)
iterable の要素を並べ替えた新たなリストを返します。
https://docs.python.org/ja/3.13/library/functions.html#sorted

sortedがdictをiterableとして扱っていることに気づくべきでした。

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.

dictはsortedに投げても上手く働いたのでsortedにエラーを吐かせたくなった。
そこでtupleのlistを投げるとエラーとならず出力を得た。

tuple_list = [(1, 2), (5, 6), (3, 9)]
tuple_list = sorted(tuple_list )
print(tuple_list )
# 出力: [(1, 2), (3, 9), (5, 6)]

Lexicographical comparison between built-in collections works as follows:
For two collections to compare equal, they must be of the same type, have the same length, and each pair of corresponding elements must compare equal (for example, [1,2] == (1,2) is false because the type is not the same).
Collections that support order comparison are ordered the same as their first unequal elements (for example, [1,2,x] <= [1,2,y] has the same value as x <= y). If a corresponding element does not exist, the shorter collection is ordered first (for example, [1,2] < [1,2,3] is true).
https://docs.python.org/3/reference/expressions.html#value-comparisons

らしい。知らなかった。


def groupAnagrams(self, strs: List[str]) -> List[List[str]]:

unicode_frequency_maps = defaultdict(list)
for s in strs:
uc_to_freq = self.count_unicodes_in_word(s)
unicode_frequency_maps[uc_to_freq].append(s)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

なんで動くのかわからなくてけっこう考えてしまいました。
動かないコードを乗せるときは明示していただけると助かります。


return list(unicode_frequency_maps.values())
```
Step2で書いたコードの
```
if not isinstance(strs, list):
raise TypeError(f'Expected: list, Actual: {type(strs)}')
```
```
assert isinstance(strs, list), f'Expected: list, Actual: {type(strs)}'
```
の方がいいかも。