1524. Number of Sub-arrays With Odd Sum
Problem
Given an array of integers arr, return the number of subarrays with an odd sum.
Since the answer can be very large, return it modulo 10⁹ + 7.
https://leetcode.com/problems/number-of-sub-arrays-with-odd-sum/
Example 1:
Input:
arr = [1,3,5]
Output:4
Explanation: All subarrays are[[1],[1,3],[1,3,5],[3],[3,5],[5]]
All sub-arrays sum are[1,4,9,3,8,5].
Odd sums are[1,9,3,5]so the answer is 4.
Example 2:
Input:
arr = [2,4,6]
Output:0
Explanation: All subarrays are[[2],[2,4],[2,4,6],[4],[4,6],[6]]
All sub-arrays sum are[2,6,12,4,10,6].
All sub-arrays have even sum and the answer is 0.
Example 3:
Input:
arr = [1,2,3,4,5,6,7]
Output:16
Constraints:
1 <= arr.length <= 10⁵1 <= arr[i] <= 100
Test Cases
class Solution:
def numOfSubarrays(self, arr: List[int]) -> int:import pytest
from solution import Solution
from solution2 import Solution as Solution2
from solution3 import Solution as Solution3
@pytest.mark.parametrize('arr, expected', [
([1,3,5], 4),
([2,4,6], 0),
([1,2,3,4,5,6,7], 16),
])
@pytest.mark.parametrize('sol', [Solution(), Solution2(), Solution3()])
def test_solution(sol, arr, expected):
assert sol.numOfSubarrays(arr) == expected
Thoughts
只有包含奇数个奇数的子数组之和是奇数。
第一个奇数是一个可行的子数组。另外如果第一个奇数左边有 l(1) 个偶数,右边有 r(1) 个偶数,第一个奇数及其左右各任意个偶数构成的子数组都可以。那么只包含第一个奇数的可行子数组总数为 (l(1) + 1) * (r(1) + 1)。可以定义 L(i) = l(i) + 1,R(i) = r(i) + 1,则为 L(1) * R(1)。
易知
R(i) = L(i+1),L(i) = R(i-1)。
第一个到第三个奇数也是一个可行的子数组,同理可知,包含且只包含这三个奇数的可行子数组总数为 L(1) * R(3))。而只包含第三个奇数的可行子数组总数为 L(3) * R(3)。可知以第三个奇数为最后一个奇数的可行子数组总数为 (L(1) + L(3)) * R(3)。
类似地,对于奇数 2k + 1,以第 2k + 1 个奇数为最后一个奇数的可行子数组总数为:
同理,对于偶数 2k,以第 2k 个奇数为最后一个奇数的可行子数组总数为:
最后把以任何奇数为最后一个奇数的可行子数组总数累加。
上边两个式子中的求和项,类似于前缀和,可以随着 k 的增加递推计算(动态规划)。
时间复杂度 O(n)。
如果事先算好所有的 L(i) 和 R(i) 并存下来,则需要 O(n) 空间,也可以边算边用,只需要 O(1) 空间。
Code
O(n) 空间:
MOD = 10 ** 9 + 7
class Solution:
def numOfSubarrays(self, arr: list[int]) -> int:
even_counts: list[int] = []
cnt = 1
for num in arr:
if num % 2 == 0:
cnt += 1
else:
even_counts.append(cnt)
cnt = 1
even_counts.append(cnt)
total = 0
prev_sum = [0, 0]
for i in range(len(even_counts) - 1):
j = i % 2
prev = even_counts[i]
prev_sum[j] += prev
total = (total + prev_sum[j] * even_counts[i+1]) % MOD
return total
O(1) 空间:
MOD = 10 ** 9 + 7
class Solution:
def numOfSubarrays(self, arr: list[int]) -> int:
total = 0
prev_sum = [0, 0]
j = 1
cnt = 1
for num in arr:
if num % 2 == 0:
cnt += 1
else:
prev_sum[j] += cnt
j = 1 - j
total = (total + prev_sum[j] * cnt) % MOD
cnt = 1
prev_sum[j] += cnt
return (total + prev_sum[1-j] * cnt) % MOD
Another Way
根据 R(i) = L(i+1),L(i) = R(i-1),重新定义 E(0) 到 E(m)(其中 m 表示 arr 中一共有 m 个奇数):
也就是 E(0) 表示第一个奇数左边的偶数个数加一;E(m) 表示最后一个(第 m 个)奇数右边的偶数个数加一;E(i) 表示第 i 个和第 i + 1 个奇数之间的偶数个数加一。
根据上边提到的「以某个奇数为最后一个奇数的可行子数组总数」,累加出来得到题目的结果,即:
最后那个展开的表达式,前两行合并可以得到 ;再与第三行合并得到 ;再与第四行合并得到 ;……。最终可以得到:
即数组 E 中所有偶数项之和,与所有奇数项之和,的乘积。
E(1) 相当于 arr 的所有前缀子数组中,只包含 1 个奇数的个数;E(3) 相当于包含 1 个 或 3 个奇数的前缀子数组个数;……;数组 E 的最后一个奇数项,表示包含奇数个奇数的前缀子数组个数。类似地 E(0) 相当于包含 0 个奇数的前缀子数组个数(注意空数组也算);……;数组 E 的最后一个偶数项,表示包含偶数个奇数的前缀子数组个数。二者之积即为题目结果。
这个乘积的数学含义可以再梳理一下。TODO
MOD = 10 ** 9 + 7
class Solution:
def numOfSubarrays(self, arr: list[int]) -> int:
e = [1, 0] # e[0] = E(0) + E(2) + E(4) + ...; e[1] = E(1) + E(3) + E(5) + ...
j = 0
for num in arr:
if num % 2 == 1: j = 1 - j
e[j] += 1
return (e[0] * e[1]) % MOD
评论需要 JavaScript。