Skip to content
Open
Show file tree
Hide file tree
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
27 changes: 27 additions & 0 deletions 78/step1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# step1 何も見ずに解く
[1,2,3] の例を考える。
i番目までのsubsetsが決まっているとすると、i + 1番目までのsubsetsは
i番目のsubsetsに、それぞれのsubsetにi + 1番目を加えたものになる。

2までのsubsetsは
[[],[1],[2],[1,2]]であり、3までのsubsetsを考えると
[[],[1],[2],[1,2]]に3を加えた[[3],[1,3],[2,3],[1,2,3]]を合わせたものになる。
要するに、i + 1番目の時点でi番目までのsubsetsはi + 1番目までのsubsetsの中でi + 1番目を入れていないsubsetsなので、入れたsubsetsを追加すれば良い。

計算量を考える。
k + 1番目の数字を入れる時は以下の処理を行う。
i番目までのsubsetsは2^k個あり、それぞれのsubsetsにk + 1番目の数字を加えた新しいsubsetをつくって元のsubsetsに追加す
subsetsの長さの平均をk / 2とすると、新しいsubsetsを追加するコストは(2^k) * (k / 2)となる。
これを1からnのkまで考えるとO(n * 2^n)になりそう。
空間計算量もsubsetの平均サイズを n / 2と考えて個数が2^nなのでO(n * 2^n)


injectを使うとサクッと書けるので以下のようになった。

```ruby
# @param {Integer[]} nums
# @return {Integer[][]}
def subsets(nums)
nums.inject([[]]) { |subsets, num| subsets.concat(subsets.map { |subset| subset + [num] }) }
Copy link

Choose a reason for hiding this comment

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

reduceっぽい関数を使ってこのように書く書き方は思いついていませんでした。勉強になります 👀
コンパクトでいいですね。

end
```
32 changes: 32 additions & 0 deletions 78/step2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# step2 他の方の解答を見る

## 再帰で解く
https://github.com/tokuhirat/LeetCode/pull/51/

pop()するところが難しいと感じたが、
subsetにnums[i]を追加して、子に参照を渡して答えを出した後にpopする、と考えれば自然か。
subsetsに追加するときに参照ではなく新しい配列を作らないとpopされて空のsubsetしか入っていないものが出力される。

```ruby
# @param {Integer[]} nums
# @return {Integer[][]}
def subsets(nums)
subsets_helper = lambda do
subsets = []
count_subsets = lambda do |i, subset|
if i == nums.size
subsets << subset[0..-1]
return
end

count_subsets.call(i + 1, subset)
subset << nums[i]
count_subsets.call(i + 1, subset)
subset.pop
end
count_subsets.call(0, [])
subsets
end
subsets_helper.call
end
```
21 changes: 21 additions & 0 deletions 78/step3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# step3 3回続けて10分以内に書いてエラーを出さなければOKとする

```ruby
# @param {Integer[]} nums
# @return {Integer[][]}
def subsets(nums)
subsets = []

Choose a reason for hiding this comment

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

関数名と同じ変数名は避けたいと思いました。冪集合なので power_set もしくは単に results はいかがでしょうか。

Copy link
Owner Author

Choose a reason for hiding this comment

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

コメントありがとうございます。power_setが良いですね。

subsets_helper = lambda do |index, subset|
if index == nums.size
subsets << subset.dup
return
end
subsets_helper.call(index + 1, subset)
subset << nums[index]
subsets_helper.call(index + 1, subset)
subset.pop
end
subsets_helper.call(0, [])
subsets
end
Copy link

Choose a reason for hiding this comment

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

読みやすいです。
bit全探索という考え方があり、それを使っても解けるので見てみると良いと思います。
整数をビットの列として解釈して、i番目のビットが1だったらi番目の要素が含まれる、という風に解釈する感じです。

def subsets(nums)
  subsets = []
  n = nums.size
  
  # 0 から 2^n - 1 までの全パターンを試す
  (0...(1 << n)).each do |bit|
    subset = []
    n.times do |i|
      # i番目のビットが立っていれば、nums[i]を含める
      subset << nums[i] if (bit >> i) & 1 == 1
    end
    subsets << subset
  end
  
  subsets
end

```
1 change: 1 addition & 0 deletions 78/step4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
## step4 レビューを受けて解答を修正