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
32 changes: 32 additions & 0 deletions 0152.Maximum-Product-Subarray/memo.md
Original file line number Diff line number Diff line change
@@ -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
書く
65 changes: 65 additions & 0 deletions 0152.Maximum-Product-Subarray/step1.py
Original file line number Diff line number Diff line change
@@ -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
52 changes: 52 additions & 0 deletions 0152.Maximum-Product-Subarray/step1_revised.py
Original file line number Diff line number Diff line change
@@ -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
16 changes: 16 additions & 0 deletions 0152.Maximum-Product-Subarray/step2_dp.py
Original file line number Diff line number Diff line change
@@ -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
30 changes: 30 additions & 0 deletions 0152.Maximum-Product-Subarray/step2_dp2.py
Original file line number Diff line number Diff line change
@@ -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))
31 changes: 31 additions & 0 deletions 0152.Maximum-Product-Subarray/step2_dp2_float.py
Original file line number Diff line number Diff line change
@@ -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))
28 changes: 28 additions & 0 deletions 0152.Maximum-Product-Subarray/step2_two_way.py
Original file line number Diff line number Diff line change
@@ -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))
50 changes: 50 additions & 0 deletions 0152.Maximum-Product-Subarray/step3.py
Original file line number Diff line number Diff line change
@@ -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