You are given a binary string boxes
of length n
, where each character represents a box: '1'
means the box contains a ball, and '0'
means it is empty.
Your task is to return an integer array answer
of length n
, where answer[i]
is the minimum number of operations needed to move all balls to the i
-th box.
In one operation, you can pick up a ball from any box and move it to any other box. The cost of moving a ball from box i
to box j
is |i - j|
(the absolute difference of their indices).
The brute-force approach is straightforward: for each box, calculate the total cost to bring every ball to that box. This means, for every index, you check all other indices, and if there's a ball, you add the distance to the current index.
However, this results in a nested loop, leading to O(n2) time complexity. For small inputs, this is fine, but for larger inputs, it's inefficient.
To optimize, we can recognize that the cost for each box can be computed efficiently by scanning from left to right and right to left, accumulating the number of balls seen so far and their total movement cost. This way, we avoid redundant calculations and bring the complexity down to O(n).
The optimized solution uses two passes over the input string:
balls
).moves
).moves
as the partial answer, then update balls
and moves
for the next position.By summing the costs from both passes, we get the minimum number of operations for each box.
This approach is efficient because:
Let's use the input boxes = "110"
.
moves = 0
, balls = 0
.'1'
), so balls += 1
. Store moves = 0
in answer[0].balls
to moves
(moves = 1
), box has a ball, so balls += 1
. Store moves = 1
in answer[1].balls
to moves
(moves = 3
), box is empty. Store moves = 3
in answer[2].After this pass, answer = [0, 1, 3]
moves = 0
, balls = 0
.moves = 3
(no change).balls
to moves
(moves = 0
), box has a ball, so balls += 1
. Add moves
to answer[1] (answer[1] = 1
).balls
to moves
(moves = 1
), box has a ball, so balls += 1
. Add moves
to answer[0] (answer[0] = 1
).Final answer = [1, 1, 3]
This matches the expected output: [1, 1, 3]
class Solution:
def minOperations(self, boxes: str) -> List[int]:
n = len(boxes)
answer = [0] * n
moves = balls = 0
# Left to right
for i in range(n):
answer[i] += moves
balls += int(boxes[i])
moves += balls
moves = balls = 0
# Right to left
for i in range(n-1, -1, -1):
answer[i] += moves
balls += int(boxes[i])
moves += balls
return answer
class Solution {
public:
vector<int> minOperations(string boxes) {
int n = boxes.size();
vector<int> answer(n, 0);
int moves = 0, balls = 0;
// Left to right
for (int i = 0; i < n; ++i) {
answer[i] += moves;
balls += boxes[i] - '0';
moves += balls;
}
moves = balls = 0;
// Right to left
for (int i = n - 1; i >= 0; --i) {
answer[i] += moves;
balls += boxes[i] - '0';
moves += balls;
}
return answer;
}
};
class Solution {
public int[] minOperations(String boxes) {
int n = boxes.length();
int[] answer = new int[n];
int moves = 0, balls = 0;
// Left to right
for (int i = 0; i < n; ++i) {
answer[i] += moves;
balls += boxes.charAt(i) - '0';
moves += balls;
}
moves = balls = 0;
// Right to left
for (int i = n - 1; i >= 0; --i) {
answer[i] += moves;
balls += boxes.charAt(i) - '0';
moves += balls;
}
return answer;
}
}
var minOperations = function(boxes) {
const n = boxes.length;
const answer = new Array(n).fill(0);
let moves = 0, balls = 0;
// Left to right
for (let i = 0; i < n; ++i) {
answer[i] += moves;
balls += Number(boxes[i]);
moves += balls;
}
moves = balls = 0;
// Right to left
for (let i = n - 1; i >= 0; --i) {
answer[i] += moves;
balls += Number(boxes[i]);
moves += balls;
}
return answer;
};
The optimized approach is much better for large inputs, as it avoids unnecessary repeated calculations.
The key insight in this problem is that the total movement cost for each box can be efficiently computed using two linear passes: one from the left and one from the right. By accumulating the number of balls and their movement costs as we go, we avoid the inefficiency of the brute-force approach and achieve an elegant O(n) solution.
This technique of using prefix and suffix accumulations is a powerful tool for similar problems where costs or counts need to be aggregated from both directions.