> 文章列表 > 个人练习-Leetcode-1588. Sum of All Odd Length Subarrays

个人练习-Leetcode-1588. Sum of All Odd Length Subarrays

个人练习-Leetcode-1588. Sum of All Odd Length Subarrays

题目链接:https://leetcode.cn/problems/sum-of-all-odd-length-subarrays/

题目大意:给出一个数组,求其中所有长度奇数的子列的所有元素和。

思路:虽然写着是简单题(暴力做可以通过),但一看就知道有简便的写法。大致思路是统计【每个位置的元素】被计入结果的【次数】。因为每个元素出现的次数只与它的位置有关而与它本身无关。开始时简单写了下前几种长度的出现情况

长度 = 1
位置 0 
次数 1
===============
长度 = 2
位置 0 1
次数 1 1
===============
长度 = 3
位置 0 1 2
次数 2 2 2
===============
长度 = 4
位置 0 1 2 3
次数 2 3 3 2
===============
长度 = 5
位置 0 1 2 3 4
次数 3 4 5 4 3

于是我天真地以为,在长度大于等于4时,在一半的长度内,相邻元素的次数只相差1(但这是错误的!)。然后写了个算法先算中心元素的次数,再让向两边其递减…然而这是错误的。

唯一正确的思想是:前一半的出现次数和后一半的出现次数是对称的。

看了题解后发现思路打开:首先,对每一个位置pos,假设其左边有left_num个元素,右边有right_num个元素。那么构成奇数长度子列的方法有

  • 【左边取奇数个,右边取奇数个】
  • 【左边取正偶数个,右边取正偶数个】
  • 【左边取正偶数个,右边取0个】
  • 【左边取0个,右边取正偶数个】
  • 【左右都取0个】

之所以写正偶数,是因为怕两边都取0个被重复计算。

那么重点就是计算

  • 从奇数个数字中取奇数长度连续子列个数
  • 从奇数个数字中取偶数长度连续子列个数
  • 从偶数个数字中取奇数长度连续子列个数
  • 从偶数个数字中取偶数长度连续子列个数

推断并不难,计算每个元素都次数的代码如下

            int N = arr.size();vector<int> cnt(N, 1);for (int pos = 0; pos < N; pos++) {int left_num = pos, right_num = N-pos-1;int left_even, left_odd;if (left_num % 2) {left_even = left_num / 2;left_odd = left_num / 2 + 1;}else {left_even = left_num / 2;left_odd = left_num / 2;}int right_even, right_odd;if (right_num % 2) {right_even = right_num / 2;right_odd = right_num / 2 + 1;}else {right_even = right_num / 2;right_odd = right_num / 2;}cnt[pos] += left_even + right_even + left_even*right_even + left_odd*right_odd;}

上述代码可以做一些赋值上的简化,并且由对称性也可以做简化。见完整代码

完整代码

class Solution {
public:int sumOddLengthSubarrays(vector<int>& arr) {int N = arr.size();vector<int> cnt(N, 1);int mid = N / 2;if (N % 2)mid = min(mid+1, N-1);for (int pos = 0; pos <= mid; pos++) {int left_num = pos, right_num = N-pos-1;int left_even = left_num / 2, left_odd = left_num / 2;if (left_num % 2) left_odd++;int right_even = right_num / 2, right_odd = right_num / 2;if (right_num % 2)right_odd++;cnt[pos] += left_even + right_even + left_even*right_even + left_odd*right_odd;}for (int pos = mid+1; pos < N; pos++)cnt[pos] = cnt[N-1-pos];   int ret = 0;for (int i = 0; i < N; i++)ret += cnt[i] * arr[i];       return ret;}
};