diff --git a/39. Combination Sum.md b/39. Combination Sum.md new file mode 100644 index 0000000..27c0626 --- /dev/null +++ b/39. Combination Sum.md @@ -0,0 +1,180 @@ +### Step1 + +- 再帰やstackでも解けそうだが、手でやるならcandidates配列をfor文で舐めるので、そのような書き方にしてみた +- 思ったよりややこしくなってしまった + - 変数名もいまいちな気もする + - ネストが深い気もするが、これ以上どうしようもないよなあ + - 「各配列の値について、今まで追加した要素についてそれぞれ、配列の値をできる限り追加」なのでややこしい + - コピーが結構発生してしまうが、これもしょうがない + - これだとキツくて、やっぱstackとか使った方が良かったか、、、 + +```python +class Solution: + def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: + all_nums_and_sum = [] + sum_is_target = [] + all_nums_and_sum.append(([], 0)) + for candidate in candidates: + added_nums_and_sum = [] + for current_nums, current_sum in all_nums_and_sum: + added_nums = current_nums.copy() + added_sum = current_sum + while True: + if added_sum + candidate > target: + break + if added_sum + candidate == target: + sum_is_target.append(added_nums + [candidate]) + break + added_nums += [candidate] + added_sum += candidate + added_nums_and_sum.append((added_nums.copy(), added_sum)) + all_nums_and_sum += added_nums_and_sum + return sum_is_target +``` + +### Step2 + +- Stackでの解法 + - 後で、stackに入れる方法をうまくやればwhile True: のネストはいらないことに気づく + - ただwhile Trueの方が手でやる操作としては素直な気もする + - まだ変数名はいまいちなきが(後で直す) + +```python +class Solution: + def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: + nums_index_sum_stack = [([], 0, 0)] + all_combinations = [] + while nums_index_sum_stack: + nums, index, current_sum = nums_index_sum_stack.pop() + if index >= len(candidates): + continue + while True: + if current_sum > target: + break + if current_sum == target: + all_combinations.append(nums.copy()) + break + nums_index_sum_stack.append((nums.copy(), index + 1, current_sum)) + nums.append(candidates[index]) + current_sum += candidates[index] + return all_combinations +``` + +- 再帰での解法 + - どうもネストが深くなる + - 今回はwhile Trueより,条件を横に置いた方がいいと思いこうした + - まだ変数名はいまいちな気がする(後で直す) + +```python +class Solution: + def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: + + # 現在の要素を含む、和がtarget以下の組み合わせと、その和を列挙 + def enumerate_sum_under_target_after_index(index): + all_nums_and_sum = [] + if index == len(candidates): + return [([], 0)] + for i in range(index + 1, len(candidates) + 1): + for current_nums, current_sum in enumerate_sum_under_target_after_index(i): + while current_sum + candidates[index] <= target: + current_nums.append(candidates[index]) + current_sum += candidates[index] + all_nums_and_sum.append((current_nums.copy(), current_sum)) + return all_nums_and_sum + + target_combinations = [] + for i in range(len(candidates)): + for current_nums, current_sum in enumerate_sum_under_target_after_index(i): + if current_sum == target: + target_combinations.append(current_nums) + return target_combinations + +``` + +https://github.com/Exzrgs/LeetCode/pull/13/files#diff-5ea0de015187769c7eee5e7ab623e2bc75c88a7c051921b35d5d5826aa0ebb26R35 + +https://github.com/SuperHotDogCat/coding-interview/pull/11/files + +- 結構みんなbacktrackで書いてるなあ(backtrackが個人的にしっくり来てないんけど、一応練習した方がいいのかな) +- stackや再帰での引き継ぎの選択肢として、自分のStep1では「今のindexをできるだけ入れて次のindexへ」とやったが + - 今の値を1個入れて今のindexに留まるものと、何も入れずに次のindexに行くものを用意 + - 今の値から最後の値まで1個ずつ入れたものをそれぞれ用意(これはちょっと不自然な気がした) +- 変数名に関する議論。_so_farというのは良いね。 +- 時間計算量、空間計算量 + - [これ](https://github.com/Mike0121/LeetCode/pull/1#discussion_r1578068513)によると、答えの数は分割数だが、(nums, index, sum)のsum List[List[int]]: + stack= [([], 0, 0)] + sum_is_target = [] + while stack: + nums_so_far, index, sum_so_far = stack.pop() + if index == len(candidates): + continue + if sum_so_far > target: + continue + if sum_so_far == target: + sum_is_target.append(nums_so_far) + continue + stack.append((nums_so_far, index + 1, sum_so_far)) + stack.append((nums_so_far + [candidates[index]], index, sum_so_far + candidates[index])) + return sum_is_target +``` + +backtrackも一応練習、上のstackを書いた後だとちょっとはしっくりくるかも + +```python + +class Solution: + def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: + combination_target_sum = [] + combination_searched = [] + + def make_combination(index, combination_sum): + if index == len(candidates): + return + if combination_sum > target: + return + if combination_sum == target: + combination_target_sum.append(combination_searched.copy()) + return + make_combination(index + 1, combination_sum) + combination_searched.append(candidates[index]) + make_combination(index, combination_sum + candidates[index]) + combination_searched.pop() + + make_combination(0, 0) + return combination_target_sum +``` + +### Step3 + +```python + +class Solution: + def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: + stack= [([], 0, 0)] + sum_is_target = [] + while stack: + nums_so_far, index, sum_so_far = stack.pop() + if index == len(candidates): + continue + if sum_so_far > target: + continue + if sum_so_far == target: + sum_is_target.append(nums_so_far) + continue + stack.append((nums_so_far, index + 1, sum_so_far)) + stack.append((nums_so_far + [candidates[index]], index, sum_so_far + candidates[index])) + return sum_is_target +```