From 6b165c47511d40d89504aa3fa2ac9a35c332cd85 Mon Sep 17 00:00:00 2001 From: shintaroyoshida20 Date: Mon, 5 May 2025 23:28:04 +0900 Subject: [PATCH 1/9] feat : #18 add the empty markdown file for creating a PR --- hash-map/group-anagrams/answer.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 hash-map/group-anagrams/answer.md diff --git a/hash-map/group-anagrams/answer.md b/hash-map/group-anagrams/answer.md new file mode 100644 index 0000000..6cd3456 --- /dev/null +++ b/hash-map/group-anagrams/answer.md @@ -0,0 +1,23 @@ +# 49. Group Anagrams + +## STEP1 + +```javascript +``` + +## STEP2 + +```javascript +``` + +## STEP3 + +```javascript +``` + +## 感想 + +### コメント集を読んで + +## その他の解法 + From 3f2acbe1e4d61a0eeaf2e16dc7449cf4a9905108 Mon Sep 17 00:00:00 2001 From: shintaroyoshida20 Date: Tue, 6 May 2025 00:12:43 +0900 Subject: [PATCH 2/9] feat : #18 add the step1/step2 --- hash-map/group-anagrams/answer.md | 42 +++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/hash-map/group-anagrams/answer.md b/hash-map/group-anagrams/answer.md index 6cd3456..6ff0f4c 100644 --- a/hash-map/group-anagrams/answer.md +++ b/hash-map/group-anagrams/answer.md @@ -3,11 +3,53 @@ ## STEP1 ```javascript +const groupAnagrams = function (strs) { + const sorted_word_to_anagrams = new Map() + for (const original_word of strs) { + const sorted_word = original_word.split('').sort().join() + if (sorted_word_to_anagrams.get(sorted_word) === undefined) { + sorted_word_to_anagrams.set(sorted_word, []) + } + const original_words = sorted_word_to_anagrams.get(sorted_word) + original_words.push(original_word) + sorted_word_to_anagrams.set(sorted_word, original_words) + } + const generator = sorted_word_to_anagrams.values() + let next = generator.next() + const ans = [] + while (!next.done) { + ans.push(next.value) + next = generator.next() + } + return ans +}; ``` ## STEP2 ```javascript +const groupAnagrams = function (strs) { + const sorted_word_to_anagrams = new Map() + for (const original_word of strs) { + const sorted_word = original_word.split('').sort().join() + if (sorted_word_to_anagrams.get(sorted_word) === undefined) { + sorted_word_to_anagrams.set(sorted_word, [original_word]) + continue + } + const original_words = sorted_word_to_anagrams.get(sorted_word) + original_words.push(original_word) + sorted_word_to_anagrams.set(sorted_word, original_words) + } + + const ans = [] + const generator = sorted_word_to_anagrams.values() + let next = generator.next() + while (!next.done) { + ans.push(next.value) + next = generator.next() + } + return ans +}; ``` ## STEP3 From 2bf2aba3a5024af4c1faaf5d469636b43ea511c6 Mon Sep 17 00:00:00 2001 From: shintaroyoshida20 Date: Tue, 6 May 2025 08:34:43 +0900 Subject: [PATCH 3/9] feat : #18 add the step1/step2 --- .../hash-map}/group-anagrams/answer.md | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) rename {hash-map => arai60/hash-map}/group-anagrams/answer.md (62%) diff --git a/hash-map/group-anagrams/answer.md b/arai60/hash-map/group-anagrams/answer.md similarity index 62% rename from hash-map/group-anagrams/answer.md rename to arai60/hash-map/group-anagrams/answer.md index 6ff0f4c..199fcbe 100644 --- a/hash-map/group-anagrams/answer.md +++ b/arai60/hash-map/group-anagrams/answer.md @@ -2,6 +2,13 @@ ## STEP1 +### 発想 + +* それぞれのwordに対して、以下を行う。 + * alphabetでsortしたワードを作成。 + * keyをsortしたword, valueを元々のwordを更新して追加する。 + * 最後にmapのvaluesのみ取り出す。 + ```javascript const groupAnagrams = function (strs) { const sorted_word_to_anagrams = new Map() @@ -55,6 +62,27 @@ const groupAnagrams = function (strs) { ## STEP3 ```javascript +const groupAnagrams = function(strs) { + const sorted_to_anagrams = new Map() + for (const original_word of strs) { + const sorted_word = original_word.split('').sort().join() + if (!sorted_to_anagrams.has(sorted_word)) { + sorted_to_anagrams.set(sorted_word, [original_word]) + continue + } + const original_words = sorted_to_anagrams.get(sorted_word) + original_words.push(original_word) + sorted_to_anagrams.set(sorted_word, original_words) + } + const ans = new Array() + const generator = sorted_to_anagrams.values() + let next = generator.next() + while (!next.done) { + ans.push(next.value) + next = generator.next() + } + return ans +}; ``` ## 感想 @@ -63,3 +91,4 @@ const groupAnagrams = function (strs) { ## その他の解法 +* `*1` MapではなくObjectを用いる方法 From 0a018abd2e491d7932b4cb8a9d8547d1337850ac Mon Sep 17 00:00:00 2001 From: shintaroyoshida20 Date: Tue, 6 May 2025 15:22:01 +0900 Subject: [PATCH 4/9] feat : #18 add two more solutions --- arai60/hash-map/group-anagrams/answer.md | 112 +++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/arai60/hash-map/group-anagrams/answer.md b/arai60/hash-map/group-anagrams/answer.md index 199fcbe..9981faa 100644 --- a/arai60/hash-map/group-anagrams/answer.md +++ b/arai60/hash-map/group-anagrams/answer.md @@ -85,10 +85,122 @@ const groupAnagrams = function(strs) { }; ``` +## 動かないコードの例 + +* (誤り) + +```javascript +const groupAnagrams = function(strs) { + const sorted_words = strs.map((word) => word.split('').sort().join('')) + const indices = [...Array(strs.length).keys()] + + indices.sort((idx_a, idx_b) => sorted_words[idx_a] - sorted_words[idx_b]) + const ans = [] + let current_sorted_word = sorted_words[indices[0]] + let anagrams = [strs[indices[0]]] + for (let i = 1; i < indices.length; i++) { + const original_word = strs[indices[i]] + const sorted_word = sorted_words[indices[i]] + if (sorted_word === current_sorted_word) { + anagrams.push(original_word) + continue + } + ans.push(anagrams) + current_sorted_word = sorted_word + anagrams = [original_word] + } + return ans +}; +``` + +* 誤りは2点で、 + * stringの引き算をしてしまっており、NaNとなりソートが正しく機能しない。 + * For文の中でansにanagramsを追加するのが、不一致が発生したタイミングなので、 + 最後のanagramsを追加ができていない。 + +* (正しい) + +``` +const groupAnagrams = function(strs) { + const sorted_words = strs.map((word) => word.split('').sort().join('')) + const indices = [...Array(strs.length).keys()] + + // UPDATED. + indices.sort((idx_a, idx_b) => sorted_words[idx_a].localeCompare(sorted_words[idx_b])) + const ans = [] + let current_sorted_word = sorted_words[indices[0]] + let anagrams = [strs[indices[0]]] + for (let i = 1; i < indices.length; i++) { + const original_word = strs[indices[i]] + const sorted_word = sorted_words[indices[i]] + if (sorted_word === current_sorted_word) { + anagrams.push(original_word) + continue + } + ans.push(anagrams) + current_sorted_word = sorted_word + anagrams = [original_word] + } + // UPDATED. + ans.push(anagrams) + return ans +}; +``` + ## 感想 ### コメント集を読んで +### 他の人のPRを読んで + ## その他の解法 * `*1` MapではなくObjectを用いる方法 + +```javascript +const groupAnagrams = function(strs) { + const sorted_to_anagrams = {} + for (const original_word of strs) { + const sorted_word = original_word.split('').sort().join() + if (sorted_to_anagrams[sorted_word] === undefined) { + sorted_to_anagrams[sorted_word] = [original_word] + continue + } + sorted_to_anagrams[sorted_word].push(original_word) + } + + return Object.values(sorted_to_anagrams) +}; +``` +* `*2` AlgoExpertにあった回答 + * 発想は、インデックスを保持した状態で、配列をAnagarmごとにソートする。 + これだと、Mapが不要になる。 + しかし、時間計算量は、他のN * W log W に比べて、N log N + * Javascriptで、rangeを作る方法はこちらのStackoverflowを参照した。 + 参考: https://stackoverflow.com/questions/3895478/does-javascript-have-a-method-like-range-to-generate-a-range-within-the-supp + +```javascript +const groupAnagrams = function(strs) { + const sorted_words = strs.map((word) => word.split('').sort().join('')) + const indices = [...Array(strs.length).keys()] + + indices.sort((idx_a, idx_b) => sorted_words[idx_a].localeCompare(sorted_words[idx_b])) + const ans = [] + let current_sorted_word = sorted_words[indices[0]] + let anagrams = [strs[indices[0]]] + for (let i = 1; i < indices.length; i++) { + const original_word = strs[indices[i]] + const sorted_word = sorted_words[indices[i]] + if (sorted_word === current_sorted_word) { + anagrams.push(original_word) + continue + } + ans.push(anagrams) + current_sorted_word = sorted_word + anagrams = [original_word] + } + ans.push(anagrams) + return ans +}; +``` + From 96f79df1ac0ed4be4e7de7bb517a823533883e05 Mon Sep 17 00:00:00 2001 From: shintaroyoshida20 Date: Tue, 6 May 2025 16:23:58 +0900 Subject: [PATCH 5/9] feat : #18 add the STEP3 with char count --- arai60/hash-map/group-anagrams/answer.md | 129 +++++++++++++++++++++-- 1 file changed, 122 insertions(+), 7 deletions(-) diff --git a/arai60/hash-map/group-anagrams/answer.md b/arai60/hash-map/group-anagrams/answer.md index 9981faa..8c98554 100644 --- a/arai60/hash-map/group-anagrams/answer.md +++ b/arai60/hash-map/group-anagrams/answer.md @@ -85,6 +85,43 @@ const groupAnagrams = function(strs) { }; ``` +* 数字をカウントする方法 + +```javascript +const groupAnagrams = function(strs) { + const valid_inputs = 'abcdefghijklmnopqrstuvwxyz' + const key_to_anagrams = {} + for (const word of strs) { + const char_count = new Array(26).fill(0) + for (const ch of word) { + if (!valid_inputs.includes(ch)) { + throw new Error("invalid input") + } + const idx = ch.charCodeAt(0) - 'a'.charCodeAt(0) + char_count[idx]++ + } + const key = arrayToVarLenQuantity(char_count) + if (key_to_anagrams[key] === undefined) { + key_to_anagrams[key] = [word] + continue + } + key_to_anagrams[key].push(word) + } + return Object.values(key_to_anagrams) +}; + +const arrayToVarLenQuantity = function(array) { + for (let i = 0; i < array.length; i++) { + if (array[i] < 16) { + array[i] = '0x0' + array[i].toString(16) + continue + } + array[i] = '0x' + array[i].toString(16) + } + return array.join('') +} +``` + ## 動かないコードの例 * (誤り) @@ -155,7 +192,7 @@ const groupAnagrams = function(strs) { ## その他の解法 -* `*1` MapではなくObjectを用いる方法 +### `*1` MapではなくObjectを用いる方法 ```javascript const groupAnagrams = function(strs) { @@ -172,12 +209,12 @@ const groupAnagrams = function(strs) { return Object.values(sorted_to_anagrams) }; ``` -* `*2` AlgoExpertにあった回答 - * 発想は、インデックスを保持した状態で、配列をAnagarmごとにソートする。 - これだと、Mapが不要になる。 - しかし、時間計算量は、他のN * W log W に比べて、N log N - * Javascriptで、rangeを作る方法はこちらのStackoverflowを参照した。 - 参考: https://stackoverflow.com/questions/3895478/does-javascript-have-a-method-like-range-to-generate-a-range-within-the-supp +### `*2` AlgoExpertにあった回答 +* 発想は、インデックスを保持した状態で、配列をAnagarmごとにソートする。 + これだと、Mapが不要になる。 + しかし、時間計算量は、他のN * W log W に比べて、N log N +* Javascriptで、rangeを作る方法はこちらのStackoverflowを参照した。 + 参考: https://stackoverflow.com/questions/3895478/does-javascript-have-a-method-like-range-to-generate-a-range-within-the-supp ```javascript const groupAnagrams = function(strs) { @@ -204,3 +241,81 @@ const groupAnagrams = function(strs) { }; ``` +### `*3` 文字数をカウントする方法もある + +``` +const groupAnagrams = function(strs) { + const valid_inputs = 'abcdefghijklmnopqrstuvwxyz' + const key_to_anagrams = {} + for (const word of strs) { + const char_count = new Array(26).fill(0) + for (const ch of word) { + if (!valid_inputs.includes(ch)) { + throw new Error("invalid input") + } + const idx = ch.charCodeAt(0) - 'a'.charCodeAt(0) + ++char_count[idx] + } + const key = JSON.stringify(char_count) + if (key_to_anagrams[key] === undefined) { + key_to_anagrams[key] = [word] + continue + } + key_to_anagrams[key].push(word) + } + return Object.values(key_to_anagrams) +}; +``` + +### `*4` 文字列のシリアライズ / エスケープシーケンス / 可変長数値表現で表現する。 + +* 文字列のシリアライズ + +```javascript + const key = JSON.stringify(char_count) +``` + +* エスケープシーケンス + +```javascript + const key = char_count.join("\t") +``` + +* 可変長数値表現 (Variable Length Quantity) + +```javascript +const groupAnagrams = function(strs) { + const valid_inputs = 'abcdefghijklmnopqrstuvwxyz' + const key_to_anagrams = {} + for (const word of strs) { + const char_count = new Array(26).fill(0) + for (const ch of word) { + if (!valid_inputs.includes(ch)) { + throw new Error("invalid input") + } + const idx = ch.charCodeAt(0) - 'a'.charCodeAt(0) + ++char_count[idx] + } + const key = arrayToVarLenQuantity(char_count) + console.log(key) + if (key_to_anagrams[key] === undefined) { + key_to_anagrams[key] = [word] + continue + } + key_to_anagrams[key].push(word) + } + return Object.values(key_to_anagrams) +}; + +const arrayToVarLenQuantity = function(array) { + for (var i = 0; i < array.length; i++) { + if (array[i] < 16) { + array[i] = "0x0" + array[i].toString(16) + continue + } + array[i] = "0x" + array[i].toString(16) + } + return array.join('') +} + +``` From 277f63905dab37712aa7baecaee273e3f19910ac Mon Sep 17 00:00:00 2001 From: shintaroyoshida20 Date: Tue, 6 May 2025 16:44:00 +0900 Subject: [PATCH 6/9] feat : #18 read thress person codebase --- arai60/hash-map/group-anagrams/answer.md | 29 +++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/arai60/hash-map/group-anagrams/answer.md b/arai60/hash-map/group-anagrams/answer.md index 8c98554..680fa69 100644 --- a/arai60/hash-map/group-anagrams/answer.md +++ b/arai60/hash-map/group-anagrams/answer.md @@ -157,7 +157,7 @@ const groupAnagrams = function(strs) { * (正しい) -``` +```javascript const groupAnagrams = function(strs) { const sorted_words = strs.map((word) => word.split('').sort().join('')) const indices = [...Array(strs.length).keys()] @@ -188,8 +188,32 @@ const groupAnagrams = function(strs) { ### コメント集を読んで +* Javascriptでは配列のnegative indexがないが、pythonではnegative indexが有効なため、 + 不正なinputが入ってきた際に誤判定されることがある。 + +* 呼び出し側/関数側で、何を前提とエラーを検知しようとしないか、何のエラーを検知するかを意識する。 + ### 他の人のPRを読んで +* Javascriptで取り組んでいるため、C++の言語仕様の議論についていけていない。 + * どこまで、C++の仕組みや議論について理解を進めるのが良いのだろうか? + * 参考: https://github.com/Ryotaro25/leetcode_first60/pull/13/files#r1636916877 + +* Pythonのdefaultdictのkeyについて、何を入力としてOKかを調べようとしたが、ドキュメントを探しても見つからず調べ方を教えて欲しいです。 + * 参考: https://github.com/olsen-blue/Arai60/pull/12/files#r1915836160 + +* olsen-blue + * PR: https://github.com/olsen-blue/Arai60/pull/12/ + * Javascriptにはない、defaultdictを使うと、mapの初期化のコードが不要となる。 + +* Ryotaro25 + * PR: https://github.com/Ryotaro25/leetcode_first60/pull/13/ + * 赤黒木のコードの理解はSKIPし、Arai60をやり終えてから取り組むこととする。 + +* hayashi-ay + * PR: https://github.com/hayashi-ay/leetcode/pull/19/ + * defaultdictのkeyに、tupleを使えることが知らなかった。 + ## その他の解法 ### `*1` MapではなくObjectを用いる方法 @@ -243,7 +267,7 @@ const groupAnagrams = function(strs) { ### `*3` 文字数をカウントする方法もある -``` +```javascript const groupAnagrams = function(strs) { const valid_inputs = 'abcdefghijklmnopqrstuvwxyz' const key_to_anagrams = {} @@ -317,5 +341,4 @@ const arrayToVarLenQuantity = function(array) { } return array.join('') } - ``` From 610a366e82ba498dc3afcfb62ba7ef727bf0d676 Mon Sep 17 00:00:00 2001 From: shintaroyoshida20 Date: Tue, 6 May 2025 16:50:51 +0900 Subject: [PATCH 7/9] feat : #18 add the impression --- arai60/hash-map/group-anagrams/answer.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/arai60/hash-map/group-anagrams/answer.md b/arai60/hash-map/group-anagrams/answer.md index 680fa69..01856e4 100644 --- a/arai60/hash-map/group-anagrams/answer.md +++ b/arai60/hash-map/group-anagrams/answer.md @@ -186,6 +186,19 @@ const groupAnagrams = function(strs) { ## 感想 +* Mapとobjectを使った方法があるが、それぞれの特徴について理解ができていなかった。調べたところ、Mapの方が優れているらしい。 + * 元々は、Objectがmapとして使われていた。 + * Mapは、以下の3点でobjectよりも優れている。 + * 1. 挿入した順序で、keyを操作(iterate)できること。 + * 2. sizeは、objectの場合自前で実装しないといけないが、mapの場合size関数を使える。 + * 3. Objectのkeyは、stringのdatatypeのみだが、mapのkeyは、Integerなどどのような型でも大丈夫なこと。 + * 参考 : https://stackoverflow.com/a/18541990 + + +* Javascriptで取り組んでいるため、C++の言語仕様の議論についていけていない。 + * どこまで、C++の仕組みや議論について理解を進めるのが良いのだろうか? + * 参考: https://github.com/Ryotaro25/leetcode_first60/pull/13/files#r1636916877 + ### コメント集を読んで * Javascriptでは配列のnegative indexがないが、pythonではnegative indexが有効なため、 @@ -195,10 +208,6 @@ const groupAnagrams = function(strs) { ### 他の人のPRを読んで -* Javascriptで取り組んでいるため、C++の言語仕様の議論についていけていない。 - * どこまで、C++の仕組みや議論について理解を進めるのが良いのだろうか? - * 参考: https://github.com/Ryotaro25/leetcode_first60/pull/13/files#r1636916877 - * Pythonのdefaultdictのkeyについて、何を入力としてOKかを調べようとしたが、ドキュメントを探しても見つからず調べ方を教えて欲しいです。 * 参考: https://github.com/olsen-blue/Arai60/pull/12/files#r1915836160 From b0601be048bd451c27339f3fe3430b2293a9629d Mon Sep 17 00:00:00 2001 From: shintaroyoshida20 Date: Tue, 6 May 2025 16:53:27 +0900 Subject: [PATCH 8/9] feat : #18 add the error handling --- arai60/hash-map/group-anagrams/answer.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arai60/hash-map/group-anagrams/answer.md b/arai60/hash-map/group-anagrams/answer.md index 01856e4..03549c1 100644 --- a/arai60/hash-map/group-anagrams/answer.md +++ b/arai60/hash-map/group-anagrams/answer.md @@ -112,6 +112,10 @@ const groupAnagrams = function(strs) { const arrayToVarLenQuantity = function(array) { for (let i = 0; i < array.length; i++) { + // 問題の入力で、strs[i].length <= 100のため。 + if (array[i] > 256) { + throw new Error("invalid input") + } if (array[i] < 16) { array[i] = '0x0' + array[i].toString(16) continue @@ -342,6 +346,10 @@ const groupAnagrams = function(strs) { const arrayToVarLenQuantity = function(array) { for (var i = 0; i < array.length; i++) { + // 問題の入力で、strs[i].length <= 100のため。 + if (array[i] > 256) { + throw new Error("invalid input") + } if (array[i] < 16) { array[i] = "0x0" + array[i].toString(16) continue From 29ca3d78407b8711a387c4d7ece81e3b0f67ebfd Mon Sep 17 00:00:00 2001 From: shintaroyoshida20 Date: Tue, 6 May 2025 16:54:47 +0900 Subject: [PATCH 9/9] feat : #18 update the solution title --- arai60/hash-map/group-anagrams/answer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arai60/hash-map/group-anagrams/answer.md b/arai60/hash-map/group-anagrams/answer.md index 03549c1..89c4e5f 100644 --- a/arai60/hash-map/group-anagrams/answer.md +++ b/arai60/hash-map/group-anagrams/answer.md @@ -85,7 +85,7 @@ const groupAnagrams = function(strs) { }; ``` -* 数字をカウントする方法 +* wordごとに、アルファベットの出現回数をカウントする方法 ```javascript const groupAnagrams = function(strs) {