【1703. 得到连续 K 个 1 的最少相邻交换次数】

来源:力扣(LeetCode)

描述:

给你一个整数数组 nums 和一个整数 k 。 nums 仅包含 0 和 1 。每一次移动,你可以选择 相邻 两个数字并将它们交换。

请你返回使 nums 中包含 k 个 连续 1 的 最少 交换次数。

示例 1:

输入:nums = [1,0,0,1,0,1], k = 2
输出:1
解释:在第一次操作时,nums 可以变成 [1,0,0,0,1,1] 得到连续两个 1 。

示例 2:

输入:nums = [1,0,0,0,0,0,1,1], k = 3
输出:5
解释:通过 5 次操作,最左边的 1 可以移到右边直到 nums 变为 [0,0,0,0,0,1,1,1] 。

示例 3:

输入:nums = [1,1,0,1], k = 2
输出:0
解释:nums 已经有连续 2 个 1 了。

提示:

  • 1 <= nums.length <= 105
  • nums[i] 要么是 0 ,要么是 1 。
  • 1 <= k <= sum(nums)

方法:贪心 + 前缀和

思路

设数组 nums 的长度为 n,其中共有 m 个 1,它们在数组 nums 中的下标分别为 p0, p1, ,…, pm−1 。假设经过若干次交换后,数组 nums 中形成的 k 个连续的 1 的下标为 q, q + 1, …, q + k − 1。根据贪心的思想,要想交换次数最少,必须满足以下两个条件:

  • 这 k 个连续的 1 的起始下标在 p 数组中是连续的,即下标为 pi , pi+1 ,…, pi+k−1
  • k 个 1 的起始下标和结尾下标是按顺序对应的,即下标为 pi 的 1 交换到 q,下标为 pi+k−1 的 1 交换到 q + k - 1 ,中间的元素也需要按照顺序。

代码:

class Solution {
public:int minMoves(vector& nums, int k) {vector g;vector preSum(1, 0);for (int i = 0; i < nums.size(); i++) {if (nums[i] == 1) {g.emplace_back(i - g.size());preSum.emplace_back(preSum.back() + g.back());}}int m = g.size(), res = INT_MAX;for (int i = 0; i <= m - k; i++) {int mid = i + k / 2;res = min(res, (1 - k % 2) * g[mid] +  \(preSum[i + k] - preSum[mid + 1]) - \(preSum[mid] - preSum[i]));}return res;}
};

执行用时:100 ms, 在所有 C++ 提交中击败了89.80%的用户
内存消耗:122.6 MB, 在所有 C++ 提交中击败了14.96%的用户
复杂度分析
时间复杂度: O(n),其中 n 是数组 nums 的长度。
空间复杂度: O(n)。
author:LeetCode-Solution