diff --git a/src/bin/step1.rs b/src/bin/step1.rs new file mode 100644 index 0000000..e0c7ada --- /dev/null +++ b/src/bin/step1.rs @@ -0,0 +1,170 @@ +// Step1 +// 目的: 方法を思いつく + +// 方法 +// 5分考えてわからなかったら答えをみる +// 答えを見て理解したと思ったら全部消して答えを隠して書く +// 5分筆が止まったらもう一回みて全部消す +// 正解したら終わり + +/* + 問題の理解 + - 整数からなる配列numsが与えられる。numsから作成することが可能なサブセットを生成して返す。 + nums=[1,2,3] + subsets=[[],[1],[2],[3],[1,2],[1,3],[2,3],[1,2,3]] + サブセットとは値に重複のない全ての可能な組み合わせだと理解した。 + + 何を考えて解いていたか + - HashSetにおいて並び順が異なり同じ値を持つ配列が重複として扱われないので、HashSetに重複管理させるのは無理。 + - 毎回ソートするのも筋が悪そう。 + - ループを回しながら重複しない配列のを作っていく。 + - 空の配列とnums,nums[i]を解の配列に詰める。O(n) + 再帰処理 + - base_case + nums.len() == 1でreturn + - recursive_case + num = nums.pop_front() + numsを解の配列に詰める + nums.push_back(num) + + 時間計算量 O(n!) + 空間計算量 O(n!) + 問題の制約から nums.length <= 10 となるので階乗の計算量でも問題ないと判断。 + そもそも、要求される解が可能な組み合わせを列挙するものなので、時間計算量はあまり改善できなず定数因数のみの改善しか行えないと思った。 + 途中でswapの方法でも書けると思ったが、まずは自然に思いついたVecDequeのpop_front(),push_back()を利用した解法で実装する。 + 重複する配列が出力され、Wrong Answerとなった。 + 重複する配列を含まずに実装する方法がすぐに思いつかないので、配列をsortしてHashSetにinsertすることで重複排除を行う。 + sortによって時間計算量が O(n! * n log n)になると考える。 + 10! * 10 log 10 = 36,288,000 となる。秒あたり10億ステップだと見積もると 36,288,000 / 10 ^ 8 = 0.36288 = 約362msとなり、現実的な実行時間ではある。 + Acceptedになった(Runtime 288ms)。しかし、他の解法ではRuntime 0msのようなので明らかにソートせずに実装する解法があることが分かる。 + step2で解法を見る。 + + 何がわからなかったか + - 重複する配列を結果に含めずに処理するアルゴリズム + + 正解してから気づいたこと + - 時間計算量の見積もりが不安だったのでGPT-5.2に聞いたところ正確にはsortの時間計算量よりも、make_subsets内のforループでcloneしているコストの方が大きくO(n! * n ^ 2)になるとのことだった。 + なので、実行時間の概算がかなり近い数値で見積もれたのはたまたまかと思った。 + + 所感 + - Wrong Answerになった時点で解答を見ようかと思ったが、時間計算量は悪化することが分かったうえでsortとHashSetによる重複排除による解法を実装してみることにしたのは良かったと思った。 + Acceptedになったから良かったということではなくて、最適解のアルゴリズムではないからナイーブな実装を試すことすらしないのは良くない癖だというコメントを思い出してこれを実践できたため。 + 答えがわからないので腕を組んで時間を浪費するのは良くないが、ナイーブな実装がわかっているならまずは実装してみて、そこを起点にアルゴリズムを改善すれば良いという考え方。 +*/ + +use std::collections::{HashSet, VecDeque}; + +pub struct Solution {} +impl Solution { + pub fn subsets(nums: Vec) -> Vec> { + if nums.is_empty() { + return vec![vec![]]; + } + + let mut subsets = HashSet::new(); + + subsets.insert(vec![]); + for i in 0..nums.len() { + subsets.insert(vec![nums[i]]); + } + + Self::make_subsets(nums, &mut subsets); + + subsets.into_iter().fold(vec![], |mut result, subset| { + result.push(subset); + result + }) + } + + fn make_subsets(nums: Vec, subsets: &mut HashSet>) { + if nums.len() == 1 { + return; + } + + let mut subset = nums.clone(); + subset.sort(); + subsets.insert(subset); + + let mut nums = VecDeque::from_iter(nums.into_iter()); + for _ in 0..nums.len() { + let num = nums.pop_front().unwrap(); + Self::make_subsets(nums.clone().into(), subsets); + nums.push_back(num); + } + } +} + +#[cfg(test)] +mod tests { + use std::collections::HashSet; + + use super::*; + + #[test] + fn playground() { + let mut set = HashSet::new(); + set.insert(&[1, 2]); + set.insert(&[2, 1]); + assert_eq!(set.len(), 2); + + let mut set = HashSet::new(); + set.insert(&[2]); + set.insert(&[2]); + assert_eq!(set.len(), 1); + } + + #[test] + fn step1_test() { + let mut subsets = Solution::subsets(vec![1, 2, 3]); + let mut expect = vec![ + vec![], + vec![1], + vec![2], + vec![1, 2], + vec![3], + vec![1, 3], + vec![2, 3], + vec![1, 2, 3], + ]; + subsets.iter_mut().for_each(|x| x.sort()); + subsets.sort(); + expect.iter_mut().for_each(|x| x.sort()); + expect.sort(); + assert_eq!(subsets, expect); + + let mut subsets = Solution::subsets(vec![3, 2, 4, 1]); + let mut expect = vec![ + vec![], + vec![3], + vec![2], + vec![2, 3], + vec![4], + vec![3, 4], + vec![2, 4], + vec![2, 3, 4], + vec![1], + vec![1, 3], + vec![1, 2], + vec![1, 2, 3], + vec![1, 4], + vec![1, 3, 4], + vec![1, 2, 4], + vec![1, 2, 3, 4], + ]; + subsets.iter_mut().for_each(|x| x.sort()); + subsets.sort(); + expect.iter_mut().for_each(|x| x.sort()); + expect.sort(); + assert_eq!(subsets, expect); + + let mut subsets = Solution::subsets(vec![0]); + let mut expect = vec![vec![], vec![0]]; + subsets.iter_mut().for_each(|x| x.sort()); + subsets.sort(); + expect.iter_mut().for_each(|x| x.sort()); + expect.sort(); + assert_eq!(subsets, expect); + + assert_eq!(Solution::subsets(vec![]), vec![vec![]]); + } +} diff --git a/src/bin/step2.rs b/src/bin/step2.rs new file mode 100644 index 0000000..1aea50c --- /dev/null +++ b/src/bin/step2.rs @@ -0,0 +1,111 @@ +// Step2 +// 目的: 自然な書き方を考えて整理する + +// 方法 +// Step1のコードを読みやすくしてみる +// 他の人のコードを2つは読んでみること +// 正解したら終わり + +// 以下をメモに残すこと +// 講師陣はどのようなコメントを残すだろうか? +// 他の人のコードを読んで考えたこと +// 改善する時に考えたこと + +/* + 他の人のコードを読んで考えたこと + https://github.com/hayashi-ay/leetcode/pull/63/changes#r1537661488 + - backtrackingの定義について。再帰の戻り掛けに何らかの処理を行うこと。 + https://ja.wikipedia.org/wiki/%E3%83%90%E3%83%83%E3%82%AF%E3%83%88%E3%83%A9%E3%83%83%E3%82%AD%E3%83%B3%E3%82%B0 + + https://github.com/Yoshiki-Iwasa/Arai60/pull/56/changes#diff-1d48419b0e20772b019b29a3bf3ff9761657623bc3ae1335b2a2f41add6b19a8R8 + - Rust実装のbacktracking + + https://github.com/ryosuketc/leetcode_arai60/pull/40/changes#diff-ad01407803e073f539072a743ce608f45e09a7eb23b9d903e6b81ad196ea9c32R10 + - bit全探索は知らないが、二通りの選択肢だと考えるとbit全探索が適用できるかもしれないという思考の流れになるのかと思った。 + + 参考した解法の理解 + https://github.com/Yoshiki-Iwasa/Arai60/pull/56/changes#diff-1d48419b0e20772b019b29a3bf3ff9761657623bc3ae1335b2a2f41add6b19a8R8 + - make_subsetsのfor-loopの中で再帰処理をする直前でsubsetにpushして、その後pop()してもとに戻している部分がbacktracking + + 所感 + - 答えのコードを見ると何をしているか、どのようにデータが遷移するかは理解できるものの、問題文からこの解法にたどり着くのには距離を感じる。 +*/ + +pub struct Solution {} +impl Solution { + pub fn subsets(nums: Vec) -> Vec> { + Self::make_subsets(&nums, &mut Vec::new()) + } + + fn make_subsets(nums: &[i32], subset: &mut Vec) -> Vec> { + let mut all_subsets = Vec::new(); + all_subsets.push(subset.to_vec()); + + for (i, num) in nums.iter().enumerate() { + subset.push(*num); + all_subsets.extend(Self::make_subsets(&nums[i + 1..], subset)); + subset.pop(); + } + all_subsets + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn step2_test() { + let mut subsets = Solution::subsets(vec![1, 2, 3]); + let mut expect = vec![ + vec![], + vec![1], + vec![2], + vec![1, 2], + vec![3], + vec![1, 3], + vec![2, 3], + vec![1, 2, 3], + ]; + subsets.iter_mut().for_each(|x| x.sort()); + subsets.sort(); + expect.iter_mut().for_each(|x| x.sort()); + expect.sort(); + assert_eq!(subsets, expect); + + let mut subsets = Solution::subsets(vec![3, 2, 4, 1]); + let mut expect = vec![ + vec![], + vec![3], + vec![2], + vec![2, 3], + vec![4], + vec![3, 4], + vec![2, 4], + vec![2, 3, 4], + vec![1], + vec![1, 3], + vec![1, 2], + vec![1, 2, 3], + vec![1, 4], + vec![1, 3, 4], + vec![1, 2, 4], + vec![1, 2, 3, 4], + ]; + subsets.iter_mut().for_each(|x| x.sort()); + subsets.sort(); + expect.iter_mut().for_each(|x| x.sort()); + expect.sort(); + assert_eq!(subsets, expect); + + let mut subsets = Solution::subsets(vec![0]); + let mut expect = vec![vec![], vec![0]]; + subsets.iter_mut().for_each(|x| x.sort()); + subsets.sort(); + expect.iter_mut().for_each(|x| x.sort()); + expect.sort(); + assert_eq!(subsets, expect); + + assert_eq!(Solution::subsets(vec![]), vec![vec![]]); + } +} diff --git a/src/bin/step2a.rs b/src/bin/step2a.rs new file mode 100644 index 0000000..f08d6f2 --- /dev/null +++ b/src/bin/step2a.rs @@ -0,0 +1,105 @@ +// Step2a +// 目的: 再帰処理をスタックの解法に書き換える練習を行う + +// 方法 +// Step1のコードを読みやすくしてみる +// 他の人のコードを2つは読んでみること +// 正解したら終わり + +// 以下をメモに残すこと +// 講師陣はどのようなコメントを残すだろうか? +// 他の人のコードを読んで考えたこと +// 改善する時に考えたこと + +/* + 所感 + https://github.com/Yoshiki-Iwasa/Arai60/pull/56/changes#r1741301219 + > ちょっとよく分かっていないんですが、ここの clone は不要ですか? + > push pop で戻しているということは。 + - ここのコメントは自分の書いたコードにも当てはまる気がするが、cloneは必要な気がする。 + ある時点のsubsetにnums[i]をpushした状態は独立してall_subsetsに加える必要があるという理解のため。 + GPT-5.2に聞いてみたところ、subsetをスタックに積む方法ではclone()は避けられなさそうだった。 + 処理を大幅に書き換えれば、for-loopの中のsubset.clone()はなくせそうだったが冗長すぎて書きたくないなと思った。 + - step2ではsubset.clone()に当たる部分は可変参照で取り回しているのでclone()していないものの、all_subsetsを毎回確保しているのでコストは結局同じになっていると思った。 +*/ + +pub struct Solution {} +impl Solution { + pub fn subsets(nums: Vec) -> Vec> { + let mut all_subsets = Vec::new(); + let mut frontier = Vec::new(); + + frontier.push((nums.as_slice(), vec![])); + while let Some((nums, mut subset)) = frontier.pop() { + all_subsets.push(subset.clone()); + + for i in 0..nums.len() { + subset.push(nums[i]); + frontier.push((&nums[i + 1..], subset.clone())); + subset.pop(); + } + } + + all_subsets + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn step2a_test() { + let mut subsets = Solution::subsets(vec![1, 2, 3]); + let mut expect = vec![ + vec![], + vec![1], + vec![2], + vec![1, 2], + vec![3], + vec![1, 3], + vec![2, 3], + vec![1, 2, 3], + ]; + subsets.iter_mut().for_each(|x| x.sort()); + subsets.sort(); + expect.iter_mut().for_each(|x| x.sort()); + expect.sort(); + assert_eq!(subsets, expect); + + let mut subsets = Solution::subsets(vec![3, 2, 4, 1]); + let mut expect = vec![ + vec![], + vec![3], + vec![2], + vec![2, 3], + vec![4], + vec![3, 4], + vec![2, 4], + vec![2, 3, 4], + vec![1], + vec![1, 3], + vec![1, 2], + vec![1, 2, 3], + vec![1, 4], + vec![1, 3, 4], + vec![1, 2, 4], + vec![1, 2, 3, 4], + ]; + subsets.iter_mut().for_each(|x| x.sort()); + subsets.sort(); + expect.iter_mut().for_each(|x| x.sort()); + expect.sort(); + assert_eq!(subsets, expect); + + let mut subsets = Solution::subsets(vec![0]); + let mut expect = vec![vec![], vec![0]]; + subsets.iter_mut().for_each(|x| x.sort()); + subsets.sort(); + expect.iter_mut().for_each(|x| x.sort()); + expect.sort(); + assert_eq!(subsets, expect); + + assert_eq!(Solution::subsets(vec![]), vec![vec![]]); + } +} diff --git a/src/bin/step2b.rs b/src/bin/step2b.rs new file mode 100644 index 0000000..08cae81 --- /dev/null +++ b/src/bin/step2b.rs @@ -0,0 +1,118 @@ +// Step2b +// 目的: bit全探索による解法を写経しておく + +// 方法 +// Step1のコードを読みやすくしてみる +// 他の人のコードを2つは読んでみること +// 正解したら終わり + +// 以下をメモに残すこと +// 講師陣はどのようなコメントを残すだろうか? +// 他の人のコードを読んで考えたこと +// 改善する時に考えたこと + +/* + 参考にした解法 + https://github.com/Yoshiki-Iwasa/Arai60/pull/56/changes#diff-dfeb6d1f9eadde6cdcec6d3be9247ad90686a5123bf798a79771a6a8f775fb35R21 + - 1<) -> Vec> { + (0..(1 << nums.len())) + .map(|subset_bits| { + (0..nums.len()) + .flat_map(|i| match (subset_bits & (1 << i)) != 0 { + true => Some(nums[i]), + false => None, + }) + .collect() + }) + .collect() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn step2b_test() { + let mut subsets = Solution::subsets(vec![1, 2, 3]); + let mut expect = vec![ + vec![], + vec![1], + vec![2], + vec![1, 2], + vec![3], + vec![1, 3], + vec![2, 3], + vec![1, 2, 3], + ]; + subsets.iter_mut().for_each(|x| x.sort()); + subsets.sort(); + expect.iter_mut().for_each(|x| x.sort()); + expect.sort(); + assert_eq!(subsets, expect); + + let mut subsets = Solution::subsets(vec![3, 2, 4, 1]); + let mut expect = vec![ + vec![], + vec![3], + vec![2], + vec![2, 3], + vec![4], + vec![3, 4], + vec![2, 4], + vec![2, 3, 4], + vec![1], + vec![1, 3], + vec![1, 2], + vec![1, 2, 3], + vec![1, 4], + vec![1, 3, 4], + vec![1, 2, 4], + vec![1, 2, 3, 4], + ]; + subsets.iter_mut().for_each(|x| x.sort()); + subsets.sort(); + expect.iter_mut().for_each(|x| x.sort()); + expect.sort(); + assert_eq!(subsets, expect); + + let mut subsets = Solution::subsets(vec![0]); + let mut expect = vec![vec![], vec![0]]; + subsets.iter_mut().for_each(|x| x.sort()); + subsets.sort(); + expect.iter_mut().for_each(|x| x.sort()); + expect.sort(); + assert_eq!(subsets, expect); + + assert_eq!(Solution::subsets(vec![]), vec![vec![]]); + } +} diff --git a/src/bin/step3.rs b/src/bin/step3.rs new file mode 100644 index 0000000..3488e13 --- /dev/null +++ b/src/bin/step3.rs @@ -0,0 +1,115 @@ +// Step3 +// 目的: 覚えられないのは、なんか素直じゃないはずなので、そこを探し、ゴールに到達する + +// 方法 +// 時間を測りながらもう一度解く +// 10分以内に一度もエラーを吐かず正解 +// これを3回連続でできたら終わり +// レビューを受ける +// 作れないデータ構造があった場合は別途自作すること + +/* + n = nums.len() + 時間計算量: O(n * 2 ^ n) + 空間計算量: O(n * 2 ^ n) + 分からなかったのでGPT-5.2に聞いた。 + 部分集合の総数は要素数nに対して 2 ^ nとなる。 + subset.clone()をループの中で毎回行っているのでO(n)が追加でコストが掛かっている。 + よって、O(n * 2 ^ n)となる。 +*/ + +/* + 1回目: 2分19秒 + 2回目: 2分10秒 + 3回目: 2分01秒 +*/ + +/* + 所感 + - 答えを見ずに書き始めたら、これまでのstepと少し違う実装になった。 + 再帰処理の外側で一度だけVecを確保して後は可変参照で取り回しているので、少し効率の良い実装になったと思った。 + - 再帰関数の中で毎回Vecを確保していない。 + - subset.clone()は結果のall_subsetsに追加するときにのみ行っている。 +*/ + +pub struct Solution {} +impl Solution { + pub fn subsets(nums: Vec) -> Vec> { + let mut all_subsets = Vec::new(); + let mut subset = Vec::new(); + + Self::make_subset(&nums, &mut subset, &mut all_subsets); + + all_subsets + } + + fn make_subset(nums: &[i32], subset: &mut Vec, all_subsets: &mut Vec>) { + all_subsets.push(subset.clone()); + + for i in 0..nums.len() { + subset.push(nums[i]); + Self::make_subset(&nums[i + 1..], subset, all_subsets); + subset.pop(); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn step3_test() { + let mut subsets = Solution::subsets(vec![1, 2, 3]); + let mut expect = vec![ + vec![], + vec![1], + vec![2], + vec![1, 2], + vec![3], + vec![1, 3], + vec![2, 3], + vec![1, 2, 3], + ]; + subsets.iter_mut().for_each(|x| x.sort()); + subsets.sort(); + expect.iter_mut().for_each(|x| x.sort()); + expect.sort(); + assert_eq!(subsets, expect); + + let mut subsets = Solution::subsets(vec![3, 2, 4, 1]); + let mut expect = vec![ + vec![], + vec![3], + vec![2], + vec![2, 3], + vec![4], + vec![3, 4], + vec![2, 4], + vec![2, 3, 4], + vec![1], + vec![1, 3], + vec![1, 2], + vec![1, 2, 3], + vec![1, 4], + vec![1, 3, 4], + vec![1, 2, 4], + vec![1, 2, 3, 4], + ]; + subsets.iter_mut().for_each(|x| x.sort()); + subsets.sort(); + expect.iter_mut().for_each(|x| x.sort()); + expect.sort(); + assert_eq!(subsets, expect); + + let mut subsets = Solution::subsets(vec![0]); + let mut expect = vec![vec![], vec![0]]; + subsets.iter_mut().for_each(|x| x.sort()); + subsets.sort(); + expect.iter_mut().for_each(|x| x.sort()); + expect.sort(); + assert_eq!(subsets, expect); + + assert_eq!(Solution::subsets(vec![]), vec![vec![]]); + } +}