LC-396. 旋转函数(前缀和+滑动窗口、动态规划)

396. 旋转函数

难度中等243

给定一个长度为 n 的整数数组 nums

假设 arrk 是数组 nums 顺时针旋转 k 个位置后的数组,我们定义 nums旋转函数 F 为:

  • F(k) = 0 * arrk[0] + 1 * arrk[1] + ... + (n - 1) * arrk[n - 1]

返回 F(0), F(1), ..., F(n-1)中的最大值

生成的测试用例让答案符合 32 位 整数。

示例 1:

输入: nums = [4,3,2,6]
输出: 26
解释:
F(0) = (0 * 4) + (1 * 3) + (2 * 2) + (3 * 6) = 0 + 3 + 4 + 18 = 25
F(1) = (0 * 6) + (1 * 4) + (2 * 3) + (3 * 2) = 0 + 4 + 6 + 6 = 16
F(2) = (0 * 2) + (1 * 6) + (2 * 4) + (3 * 3) = 0 + 6 + 8 + 9 = 23
F(3) = (0 * 3) + (1 * 2) + (2 * 6) + (3 * 4) = 0 + 2 + 12 + 12 = 26
所以 F(0), F(1), F(2), F(3) 中的最大值是 F(3) = 26 。

示例 2:

输入: nums = [100]
输出: 0

提示:

  • n == nums.length
  • 1 <= n <= 105
  • -100 <= nums[i] <= 100

前缀和 + 滑动窗口

题解:https://leetcode.cn/problems/rotate-function/solution/by-ac_oier-sxbi/

题目要对「旋转数组」做逻辑,容易想到将 n u m s nums nums 进行复制拼接,得到长度为 2 ∗ n 2 * n 2n 的新数组,在新数组上任意一个长度为 $n $ 的滑动窗口都对应了一个旋转数组。

当窗口往后移动一位,也就是窗口的右端点来到 i + n i + n i+n 的位置,左端点来到 i + 1 i + 1 i+1 的位置。

不难发现,随着窗口的逐步右移,每一位公共部分的权值系数都会进行减一。

公共部分的差值为 ∑ i d x = i + 1 i + n − 1 n u m s [ i d x ] \sum_{idx = i + 1}^{i + n - 1}nums[idx] idx=i+1i+n1nums[idx],这引导我们可以使用前缀和进行优化。

至此,我们从旧窗口到新窗口的过渡,都是 O ( 1 ) O(1) O(1),整体复杂度为 O ( n ) O(n) O(n)

实现上,我们并不需要真正对 n u m s nums nums 进行复制拼接,而只需要在计算前缀和数组 s u m sum sum 进行简单的下标处理即可。

class Solution {
    public int maxRotateFunction(int[] nums) {
        int n = nums.length;
        int[] sum = new int[n*2+1];
        for(int i = 1; i <= 2*n; i++){
            // (i-1)%n,对下标进行处理,以实现旋转数组2*n的前缀和
            sum[i] = sum[i-1] + nums[(i-1)%n];
        }
        int ans = 0;
        for(int i = 1; i <= n; i++){
            //根据F的定义,先把初始值求出来
            ans += nums[i-1]*(i-1);
        }
        for(int i = n+1,cur = ans; i < 2*n; i++){
            cur += nums[(i-1) % n]*(n-1); // 加上出现在窗口右端点的值(n - 1) * arrk[n - 1]
            cur -= sum[i-1] - sum[i-n]; // 权重系数都减了1 = 减去窗口内的和
            if(cur > ans) ans = cur;
        }
        return ans;
    }
}

找规律+动态规划

// 找规律题
// sum:                            a0 * 1 + a1 * 1 + a2 * 1 + a3 * 1

// f(0):                              a0 * 0 + a1 * 1 + a2 * 2 + a3 * 3
// f(0) + sum:                        a0 * 1 + a1 * 2 + a2 * 3 + a3 * 4  

// f(1):                     a3 * 0 + a0 * 1 + a1 * 2 + a2 * 3              f(1) = f(0) + sum - 4*a[4-1]
// f(1) + sum:               a3 * 1 + a0 * 2 + a1 * 3 + a2 * 4

// f(2):            a2 * 0 + a3 * 1 + a0 * 2 + a1 * 3                   f(2) = f(1) + sum - 4*a[4-2]
// f(2) + sum:      a2 * 1 + a3 * 2 + a0 * 3 + a1 * 4 

// f(3):  a1 * 0 +  a2 * 1 + a3 * 2 + a0 * 3                        f(3) = f(2) + sum - 4*a[4-3]

// 由上可得f(n) = f(n - 1) + sum - len * a[len - n]
class Solution:
    def maxRotateFunction(self, nums: List[int]) -> int:
        n = len(nums)
        cur = sum([i * nums[i] for i in range(n)])
        ans = cur
        total = sum(nums)

        for i in range(n - 1):
            cur = cur + n * nums[i] - total
            if cur > ans:
                ans = cur

        return ans  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值