diff --git a/0152.Maximum-Product-Subarray/memo.md b/0152.Maximum-Product-Subarray/memo.md new file mode 100644 index 0000000..69c3aff --- /dev/null +++ b/0152.Maximum-Product-Subarray/memo.md @@ -0,0 +1,32 @@ +# 152. Maximum Product Subarray + +## step1 + +24mほど書いたが、デバッグして答えを合わせた。かなりコードが冗長になった。 + +計算量 時間:O(n):空間:O(n) + +改善する: step1_revised。iteratorを使って空間をO(1)にした。 + +## step2 + + +Solutionsを見るとDPで解くこともできるようだ。Kadaneのアルゴリズムに似ている?: step2_dp.py + +## 他の人のコード + +https://discord.com/channels/1084280443945353267/1196498607977799853/1358736384604766238 + +DPの書き方が若干違った。0を特別扱いするか否か。 + +float版に拡張もできる(初期化を変える必要はないように思うが)。テストの途中を追うことでやっと理解できた。minus_maxは-1をかけたときに最大値になるものを保持している。 + +step2_two_way.pyは、「0を含まない区間に負の数が奇数個の場合には、最右または最左の負の数までの最大値が答えとなる」ことを利用し、左と右で2回計算して答えを求めている。 + +Pythonに変換しながらロジックを追った。 + +自分の解法よりロジックがわかりやすくコードもシンプル。 + + +## step3 +書く diff --git a/0152.Maximum-Product-Subarray/step1.py b/0152.Maximum-Product-Subarray/step1.py new file mode 100644 index 0000000..38fc2fa --- /dev/null +++ b/0152.Maximum-Product-Subarray/step1.py @@ -0,0 +1,65 @@ +import math + + +class Solution: + def maxProduct(self, nums: list[int]) -> int: + if not nums: + raise ValueError("input is empty") + + def max_product_helper(first, last): + if first == last: + return nums[first] + + num_negative = 0 + for n in nums[first : last + 1]: + if n < 0: + num_negative += 1 + if num_negative % 2 == 0: + return math.prod(nums[first : last + 1]) + + first_negative = first + product_up_to_first_negative = nums[first_negative] + while first_negative <= last and nums[first_negative] > 0: + first_negative += 1 + if first_negative <= last: + product_up_to_first_negative *= nums[first_negative] + + if first_negative > last: + return product_up_to_first_negative + if first_negative == last: + return math.prod(nums[first:first_negative]) + + last_negative = last + product_until_last_negative = nums[last_negative] + while last_negative >= first and nums[last_negative] > 0: + last_negative -= 1 + if last_negative >= first: + product_until_last_negative *= nums[last_negative] + + if abs(product_up_to_first_negative) < abs(product_until_last_negative): + print( + first_negative, last, math.prod(nums[first_negative + 1 : last + 1]) + ) + return math.prod(nums[first_negative + 1 : last + 1]) + else: + print(first, last_negative, math.prod(nums[first:last_negative])) + return math.prod(nums[first:last_negative]) + + max_product = nums[-1] + + begin = 0 + while begin < len(nums): + while begin < len(nums) and nums[begin] == 0: + begin += 1 + if begin == len(nums): + continue + end = begin + 1 + while end < len(nums) and nums[end] != 0: + end += 1 + print(begin, end, max_product_helper(begin, end - 1)) + max_product = max(max_product, max_product_helper(begin, end - 1)) + begin = end + 1 + if begin < len(nums): + max_product = max(max_product, 0) + + return max_product diff --git a/0152.Maximum-Product-Subarray/step1_revised.py b/0152.Maximum-Product-Subarray/step1_revised.py new file mode 100644 index 0000000..1c36d9b --- /dev/null +++ b/0152.Maximum-Product-Subarray/step1_revised.py @@ -0,0 +1,52 @@ +import math + + +class Solution: + def maxProduct(self, nums: list[int]) -> int: + if not nums: + raise ValueError("input is empty") + + max_product = nums[0] + if 0 in nums: + max_product = max(max_product, 0) + + def max_product_no_zero_between(begin: int, end: int) -> int | float: + if begin >= end: + return -math.inf + if end - begin == 1: + return nums[begin] + + num_negative = sum(1 for i in range(begin, end) if nums[i] < 0) + + if num_negative % 2 == 0: + return math.prod(nums[i] for i in range(begin, end)) + + first_negative_index = next(i for i in range(begin, end) if nums[i] < 0) + last_negative_index = next( + i for i in range(end - 1, begin - 1, -1) if nums[i] < 0 + ) + + prod_after_first = math.prod( + nums[i] for i in range(first_negative_index + 1, end) + ) + prod_before_last = math.prod( + nums[i] for i in range(begin, last_negative_index) + ) + + return max(prod_after_first, prod_before_last) + + begin = 0 + for i in range(len(nums)): + if nums[i] == 0: + if begin < i: + max_product = max( + max_product, max_product_no_zero_between(begin, i) + ) + begin = i + 1 + + if begin < len(nums): + max_product = max( + max_product, max_product_no_zero_between(begin, len(nums)) + ) + + return max_product diff --git a/0152.Maximum-Product-Subarray/step2_dp.py b/0152.Maximum-Product-Subarray/step2_dp.py new file mode 100644 index 0000000..2424c69 --- /dev/null +++ b/0152.Maximum-Product-Subarray/step2_dp.py @@ -0,0 +1,16 @@ +class Solution: + def maxProduct(self, nums: list[int]) -> int: + max_product = nums[0] + sub_max_product = nums[0] + sub_min_product = nums[0] + + for n in nums[1:]: + if n < 0: + sub_max_product, sub_min_product = sub_min_product, sub_max_product + + sub_max_product = max(n, sub_max_product * n) + sub_min_product = min(n, sub_min_product * n) + + max_product = max(max_product, sub_max_product) + + return max_product diff --git a/0152.Maximum-Product-Subarray/step2_dp2.py b/0152.Maximum-Product-Subarray/step2_dp2.py new file mode 100644 index 0000000..9c400e9 --- /dev/null +++ b/0152.Maximum-Product-Subarray/step2_dp2.py @@ -0,0 +1,30 @@ +class Solution: + def maxProduct(self, nums: list[int]) -> int: + if len(nums) == 1: + return nums[0] + + result = 0 + plus_max = 0 + minus_max = 0 + + for num in nums: + if num == 0: + plus_max = 0 + minus_max = 0 + else: + if plus_max == 0: + plus_max = 1 + if num < 0: + plus_max, minus_max = minus_max, plus_max + + plus_max *= num + minus_max *= num + + result = max(result, plus_max) + + return result + + +sol = Solution() +nums = [0.1, 0.2, 0.1] +print(sol.maxProduct(nums)) diff --git a/0152.Maximum-Product-Subarray/step2_dp2_float.py b/0152.Maximum-Product-Subarray/step2_dp2_float.py new file mode 100644 index 0000000..907666f --- /dev/null +++ b/0152.Maximum-Product-Subarray/step2_dp2_float.py @@ -0,0 +1,31 @@ +class Solution: + def maxProduct(self, nums: list[float]) -> float: + if len(nums) == 1: + return nums[0] + + result = 0 + plus_max = 0 + minus_max = 0 + + for num in nums: + if num == 0: + plus_max = 0 + minus_max = 0 + continue + if plus_max < 1: + plus_max = 1 + if num < 0: + plus_max, minus_max = minus_max, plus_max + + plus_max *= num + minus_max *= num + # print(plus_max, minus_max) + + result = max(result, plus_max) + + return result + + +sol = Solution() +nums = [0.1, -0.2, -2] +print(sol.maxProduct(nums)) diff --git a/0152.Maximum-Product-Subarray/step2_two_way.py b/0152.Maximum-Product-Subarray/step2_two_way.py new file mode 100644 index 0000000..c44142d --- /dev/null +++ b/0152.Maximum-Product-Subarray/step2_two_way.py @@ -0,0 +1,28 @@ +import math + + +class Solution: + def maxProduct(self, nums: list[int]) -> int: + def update_max_product(nums: list[int], max_product: int) -> int: + product = 1 + for num in nums: + if product == 0: + product = 1 + + product *= num + max_product = max(max_product, product) + + return max_product + + max_product = -math.inf + max_product = update_max_product(nums, max_product) + + nums.reverse() + max_product = update_max_product(nums, max_product) + + return max_product + + +sol = Solution() +nums = [0.1, 0.2, 0.1] +print(sol.maxProduct(nums)) diff --git a/0152.Maximum-Product-Subarray/step3.py b/0152.Maximum-Product-Subarray/step3.py new file mode 100644 index 0000000..73604c7 --- /dev/null +++ b/0152.Maximum-Product-Subarray/step3.py @@ -0,0 +1,50 @@ +import math + + +class Solution: + def maxProduct(self, nums: list[int]) -> int: + def update_max_product(nums, max_product): + product = 1 + for num in nums: + if product == 0: + product = 1 + + product *= num + max_product = max(max_product, product) + + return max_product + + max_product = -math.inf + max_product = update_max_product(nums, max_product) + + nums.reverse() + max_product = update_max_product(nums, max_product) + + return max_product + + +class Solution: + def maxProduct(self, nums: list[int]) -> int: + if len(nums) == 1: + return nums[0] + + max_product = 0 + plus_max = 0 + minus_max = 0 + + for num in nums: + if num == 0: + plus_max = 0 + minus_max = 0 + else: + if plus_max == 0: + plus_max = 1 + if num < 0: + plus_max, minus_max = minus_max, plus_max + + plus_max *= num + minus_max *= num + + max_product = max(max_product, plus_max) + + return max_product