个人练习-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;
    }
};

你可能感兴趣的:(leetcode,算法,数据结构)