题目描述
给你一个整数数组 nums 和一个整数 x 。每一次操作时,你应当移除数组 nums 最左边或最右边的元素,然后从 x 中减去该元素的值。请注意,需要 修改 数组以供接下来的操作使用。
如果可以将 x 恰好 减到 0 ,返回最小操作数 ;否则,返回 -1 。
示例 1:
输入:nums = [1,1,4,2,3], x = 5
输出:2
解释:最佳解决方案是移除后两个元素,将 x 减到 0 。
示例 2:
输入:nums = [5,6,7,8,9], x = 4
输出:-1
示例 3:
输入:nums = [3,2,20,1,1,3], x = 10
输出:5
解释:最佳解决方案是移除后三个元素和前两个元素(总共 5 次操作),将 x 减到 0 。
提示:
1 <= nums.length <= 1051 <= nums[i] <= 1041 <= x <= 109
解决方案:
核心转换思路:
-
原本问题:从数组两端 删除元素,使删除的元素和等于
x -
等价问题:找到数组中间一个连续子数组 ,使其和等于
total - x -
这样两端删除的次数最少 = 数组总长度 - 中间子数组长度最大
算法步骤:
-
计算数组总和
total -
计算中间子数组的目标和:
midSum = total - x -
如果
midSum < 0:不可能,返回 -1 -
如果
midSum == 0:需要删除所有元素,返回数组长度 -
用滑动窗口找和为
midSum的最长连续子数组 -
如果找到:返回
len - 最长子数组长度 -
如果没找到:返回 -1
滑动窗口细节:
-
维护窗口
[left, i]的和为sum -
当
sum > midSum时,从左收缩窗口 -
当
sum == midSum时,更新最大窗口长度 -
窗口长度越大,两端删除的元素就越少
时间复杂度:O(n),每个元素最多进出窗口一次
举例:
cppnums = [1,1,4,2,3], x = 5 total = 11, midSum = 6 最长和为6的子数组:[1,1,4],长度=3 最小操作次数 = 5 - 3 = 2 删除两端元素:[2,3]
结果:返回删除两端元素的最少操作次数,如果不可能则返回 -1。
函数源码:
cppclass Solution { public: int minOperations(vector<int>& nums, int x) { int len =nums.size(); int ans=0; int left=0; int midSum=accumulate(nums.begin(),nums.end(),0) - x;//求和 if(midSum<0) return -1; if(midSum==0) return len; int sum=0; for(int i=0;i<len;i++){ sum+=nums[i]; while(sum>midSum){ sum-=nums[left]; left+=1; } if(sum==midSum) ans=max(ans,i-left+1); } return ans==0? -1:len-ans; } };