Minimum Adjacent Swaps for K Consecutive Ones
LeetCode 1805 | Difficulty: Hardβ
HardProblem Descriptionβ
You are given an integer array, nums, and an integer k. nums comprises of only 0's and 1's. In one move, you can choose two adjacent indices and swap their values.
Return the minimum number of moves required so that nums has k consecutive 1's.
Example 1:
Input: nums = [1,0,0,1,0,1], k = 2
Output: 1
Explanation: In 1 move, nums could be [1,0,0,0,1,1] and have 2 consecutive 1's.
Example 2:
Input: nums = [1,0,0,0,0,0,1,1], k = 3
Output: 5
Explanation: In 5 moves, the leftmost 1 can be shifted right until nums = [0,0,0,0,0,1,1,1].
Example 3:
Input: nums = [1,1,0,1], k = 2
Output: 0
Explanation: nums already has 2 consecutive 1's.
Constraints:
- `1 <= nums.length <= 10^5`
- `nums[i]` is `0` or `1`.
- `1 <= k <= sum(nums)`
Topics: Array, Greedy, Sliding Window, Prefix Sum
Approachβ
Sliding Windowβ
Maintain a window [left, right] over the array/string. Expand right to include new elements, and shrink left when the window violates constraints. Track the optimal answer as the window slides.
Finding subarrays/substrings with a property (max length, min length, exact count).
Greedyβ
At each step, make the locally optimal choice. The challenge is proving the greedy choice leads to a global optimum. Look for: can I sort by some criterion? Does choosing the best option now ever hurt future choices?
Interval scheduling, activity selection, minimum coins (certain denominations), Huffman coding.
Prefix Sumβ
Build a prefix sum array where prefix[i] = sum of elements from index 0 to i. Then any subarray sum [l..r] = prefix[r] - prefix[l-1]. This turns range sum queries from O(n) to O(1).
Subarray sum queries, counting subarrays with a target sum, range computations.
Solutionsβ
Solution 1: C# (Best: 264 ms)β
| Metric | Value |
|---|---|
| Runtime | 264 ms |
| Memory | 46.3 MB |
| Date | 2021-09-13 |
public class Solution {
public int MinMoves(int[] nums, int k) {
List<int> p = new List<int>() {0};
for (int i = 0; i < nums.Length; i++)
{
if (nums[i] == 1)
{
p.Add(i);
}
}
int n = p.Count;
int[] presum = new int[n];
for (int i = 1; i < n; i++)
{
presum[i] = p[i] + presum[i-1];
}
int result = Int32.MaxValue;
for (int l=1, r=k; r<n; l++, r++)
{
int mid = (l+r)/2;
int radius = mid-l;
int right = presum[r]-presum[mid];
int left = presum[mid-1] - presum[l-1];
int substract = (1+radius)*radius;
if(k%2 == 0)
{
substract += p[mid];
substract += (1+radius);
}
result = Math.Min(result, right-left-substract);
}
return result;
}
}
π 1 more C# submission(s)
Submission (2021-09-13) β 447 ms, 46.1 MBβ
public class Solution {
public int MinMoves(int[] nums, int k) {
List<int> p = new List<int>() {};
for (int i = 0; i < nums.Length; i++)
{
if (nums[i] == 1)
{
p.Add(i + 1);
}
}
int n = p.Count;
int[] presum = new int[n + 1];
for (int i = 0; i < p.Count; i++)
{
presum[i+1] = p[i] + presum[i];
}
int result = Int32.MaxValue;
int radius, right, left;
if(k%2 == 1)
{
radius = (k-1)/2;
for (int i = radius; i < n-radius; i++)
{
right = presum[i+radius+1]-presum[i+1];
left = presum[i] - presum[i-radius];
result = Math.Min(result, right-left);
}
return result-radius*(radius+1);
}
else
{
radius = (k-2)/2;
for (int i = radius; i < n-radius-1; i++)
{
right = presum[i+radius+2]-presum[i+1];
left = presum[i] - presum[i-radius];
result = Math.Min(result, right-left-p[i]);
}
return result-radius*(radius+1)-(radius+1);
}
}
}
Complexity Analysisβ
| Approach | Time | Space |
|---|---|---|
| Sliding Window | $O(n)$ | $O(k)$ |
| Prefix Sum | $O(n)$ | $O(n)$ |
Interview Tipsβ
- Break the problem into smaller subproblems. Communicate your approach before coding.
- Clarify what makes a window "valid" and what triggers expansion vs shrinking.
- LeetCode provides 3 hint(s) for this problem β try solving without them first.
π‘ Hints
Hint 1: Choose k 1s and determine how many steps are required to move them into 1 group.
Hint 2: Maintain a sliding window of k 1s, and maintain the steps required to group them.
Hint 3: When you slide the window across, should you move the group to the right? Once you move the group to the right, it will never need to slide to the left again.