滑动窗口(4)—将x减到0的最⼩操作数
文章目录
- 题目解析
- 方法一:滑动窗口
- 方法二:哈希表 + 前缀和
- 附Java代码
力扣题目:将x减到0的最⼩操作数
题目解析
方法一:滑动窗口
算法思路:
题⽬要求的是数组「左端+右端」两段连续的、和为
sum(nums) - x
的最⻓数组。此时,就是熟
悉的「滑动窗⼝」问题了。
class Solution
{
public:
int minOperations(vector<int>& nums, int x)
{
int sum = 0;
for(int a : nums) sum += a;
int target = sum - x;
//
细节问题
if(target < 0) return -1;
int ret = -1;
for(int left = 0, right = 0, tmp = 0; right < nums.size(); right++)
{
tmp += nums[right]; //
进窗⼝
while(tmp > target) //
判断
tmp -= nums[left++]; //
出窗⼝
if(tmp == target) //
更新结果
ret = max(ret, right - left + 1);
}
if(ret == -1) return ret;
else return nums.size() - ret;
}
};
方法二:哈希表 + 前缀和
我们可以将问题转换为求中间连续子数组的最大长度,使得子数组的和为 x=sum(nums)−x。
定义一个哈希表 vis,其中 vis[s] 表示前缀和为 s 的最小下标。
遍历数组 nums,对于每个元素 nums[i],我们先将 nums[i] 加到前缀和 s 上,如果哈希表中不存在 s,则将其加入哈希表,其值为当前下标 i。然后我们判断 s−x 是否在哈希表中,如果存在,则说明存在一个下标 j,使得 nums[j+1,…i] 的和为 x,此时我们更新答案的最小值,即 ans=min(ans,n−(i−j))。
遍历结束,如果找不到满足条件的子数组,返回 −1,否则返回 ans。
class Solution {
public:
int minOperations(vector<int>& nums, int x) {
x = accumulate(nums.begin(), nums.end(), 0) - x;
unordered_map<int, int> vis{{0, -1}};
int n = nums.size();
int ans = 1 << 30;
for (int i = 0, s = 0; i < n; ++i) {
s += nums[i];
if (!vis.count(s)) {
vis[s] = i;
}
if (vis.count(s - x)) {
int j = vis[s - x];
ans = min(ans, n - (i - j));
}
}
return ans == 1 << 30 ? -1 : ans;
}
};
附Java代码
class Solution {
public int minOperations(int[] nums, int x) {
int n = nums.length;
int sum = Arrays.stream(nums).sum();
if (sum < x) {
return -1;
}
int right = 0;
int lsum = 0, rsum = sum;
int ans = n + 1;
for (int left = -1; left < n; ++left) {
if (left != -1) {
lsum += nums[left];
}
while (right < n && lsum + rsum > x) {
rsum -= nums[right];
++right;
}
if (lsum + rsum == x) {
ans = Math.min(ans, (left + 1) + (n - right));
}
}
return ans > n ? -1 : ans;
}
}