给你一个正整数数组 arr ,请你计算所有可能的奇数长度子数组的和。
子数组 定义为原数组中的一个连续子序列。
请你返回 arr 中 所有奇数长度子数组的和 。
我们只需要枚举所有的奇数长度的子数组即可。
class Solution {
public:
int sumOddLengthSubarrays(vector<int>& arr) {
int res = 0;
for(int i = 0; i < arr.size(); ++i){
for(int step = 1; i + step <= arr.size(); step += 2){
res += accumulate(arr.begin() + i, arr.begin() + i + step, 0);
}
}
return res;
}
};
在上面的解法中,accumulate的本质就是在计算从 arr[i] 到 arr[i + step - 1] 的连续子数组的和。计算连续子数组的和,很容想到可以使用前缀和(Prefix Sum)的方式。使用 O(n) 的时间可以预处理前缀和数组,之后使用 O(1) 的时间即可计算出一个连续子数组的和。
时间复杂度是 O(n^2) 的,空间复杂度是 O(n) 的。
class Solution {
public:
int sumOddLengthSubarrays(vector<int>& arr) {
vector<int> persum = {0};
for(int e: arr)
persum.push_back((persum.back() + e));
int res = 0;
for(int i = 0; i < arr.size(); ++i){
for(int step = 1; step + i <= arr.size(); step += 2){
res += persum[i + step] - persum[i];
}
}
return res;
}
};
这个问题有 O(n) 的解法。
- odd奇数,even偶数
- 对于每个元素i(数组中下标为i)来说,要构成奇数长度的子数组
即 i左边的元素个数left+i本身自己一个+右边元素的个数right=奇数
即 left+right=偶数 - 满足a+b=偶数就只有两种情况
- 奇数+奇数=偶数
- 偶数+偶数=偶数
- 所以只需要求得i左边可以选择奇数长度的可能有多少种,即left_odd,同样求右边奇数right_odd
就可以求出策略1有多少种可能
所以只需要求得i左边可以选择偶数长度的可能有多少种,即left_odd,同样求右边偶数right_odd
就可以求出策略1有多少种可能,注意0也算选择的一种可能 - 即元素i在所有奇数长度子数组出现的次数总和是
left_oddright_odd+left_evenright_even - 元素i左边元素共有i个,右边元素共有siz-i-1个
class Solution {
public:
int sumOddLengthSubarrays(vector& arr) {
int res = 0;
for(int i = 0; i < arr.size(); i ++){
int left = i + 1, right = arr.size() - i,
left_even = (left + 1) / 2, right_even = (right + 1) / 2,
left_odd = left / 2, right_odd = right / 2;
res += (left_even * right_even + left_odd * right_odd) * arr[i];
}
return res;
}
};
O(n) 的解法可以应对 10^6 乃至 10^7 的数字规模。如果这道题的数字规模是这样的,那么上面两种解法都将超时,这个问题也就变成了一个 Medium 甚至是 Hard 的问题了吧:)
欢迎关注公众号,往期内容也很精彩。每周更新面试高频算法题。从思路,代码,时间复杂度等多方面进行分析。
后台点击电子书可以获得:halfrost大神的孔雀书,经典巨著:算法导论。