题目链接
Leetcode.1658 将 x 减到 0 的最小操作数
题目描述
给你一个整数数组 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 < = n u m s . l e n g t h < = 1 0 5 1 <= nums.length <= 10^5 1<=nums.length<=105
1 < = n u m s [ i ] < = 1 0 4 1 <= nums[i] <= 10^4 1<=nums[i]<=104
1 < = x < = 1 0 9 1 <= x <= 10^9 1<=x<=109
分析:
本题要求我们从数组的两端取数,能够用最少的数将x减为0,返回这个最少的次数。
由于正向思考,比较难以处理,所以这里可以逆向思考:数组的和为 s u m sum sum , t a r g e t = s u m − x target = sum - x target=sum−x,那么我们就用滑动窗口维护一个和为 target 的最大区间,那么此时数组的长度 n 减去 此时target区间的最大长度 就是我们要求的答案。
- 时间复杂度: O ( n ) O(n) O(n)
C++代码:
class Solution {
public:
int minOperations(vector<int>& nums, int x) {
int n = nums.size();
int ans = 1e9;
int sum = accumulate(nums.begin(),nums.end(),0);
if(sum < x) return -1;
sum -= x;
int m = 0;
for(int i = 0,j = 0;j < n;j++){
m += nums[j];
if(m > sum){
while(m > sum){
m -= nums[i++];
}
}
if(m == sum){
ans = min(ans,n - (j - i + 1));
}
}
return ans == 1e9 ? -1 : ans;
}
};
Java代码:
class Solution {
public int minOperations(int[] nums, int x) {
int n = nums.length;
int sum = 0;
for(int e:nums) sum += e;
int target = sum - x;
//特判数组的和 sum < x 的情况,这时直接返回 -1
if(target < 0) return -1;
int m = 0;
int len = -1;
for(int i = 0,j = 0;j < n;j++){
m += nums[j];
while(m > target){
m -= nums[i++];
}
//当区间的和 等于 target时 才更新数组的长度
if(m == target){
len = Math.max(len,j - i + 1);
}
}
return len == -1 ? -1 : n - len;
}
}
相似题目
Leetcode.3 无重复字符的最长子串
Leetcode.209 长度最小的子数组
Leetcode.713 乘积小于 K 的子数组
Leetcode.2516 每种字符至少取 K 个