1864. Minimum Number of Swaps to Make the Binary String Alternating
Problem Description
You are given a binary string s
that contains only characters '0' and '1'. Your task is to find the minimum number of character swaps needed to make the string alternating, or return -1
if it's impossible.
An alternating string is one where no two adjacent characters are the same. For example:
"010"
and"1010"
are alternating strings (characters alternate between 0 and 1)"0100"
is not alternating (has two consecutive 0s)
When performing a swap, you can exchange any two characters in the string, even if they are not adjacent to each other.
The key insight is that an alternating string can only have two patterns:
- Starting with '0': "0101010..."
- Starting with '1': "1010101..."
For a string to be convertible to an alternating pattern:
- If the string has even length, it must have equal numbers of 0s and 1s
- If the string has odd length, one character must appear exactly one more time than the other
The solution counts how many positions need to be corrected to match each possible alternating pattern, then determines the minimum swaps needed (each swap fixes two positions, so the number of swaps is half the number of mismatched positions).
Intuition
To understand how to solve this problem, let's think about what an alternating string looks like and what constraints we have.
First, we need to recognize that there are only two possible alternating patterns for any string:
- Pattern A: "0101010..." (starting with 0)
- Pattern B: "1010101..." (starting with 1)
For a string to be transformable into an alternating pattern, we need the right count of 0s and 1s. If the string has length n
:
- For even
n
: we need exactlyn/2
zeros andn/2
ones - For odd
n
: we need either(n+1)/2
zeros and(n-1)/2
ones, or vice versa
If the counts don't match these requirements, it's impossible to create an alternating string no matter how many swaps we make, so we return -1
.
Now, how do we count the minimum swaps needed? The key insight is that when we swap two characters, we're fixing two positions at once. For example, if position 2 needs a '0' but has a '1', and position 5 needs a '1' but has a '0', one swap fixes both positions.
To calculate the swaps for a target pattern:
- Count how many positions have the wrong character compared to the target pattern
- Since each swap fixes exactly 2 positions, the number of swaps needed is half of the mismatched positions
When we have equal counts of 0s and 1s, we could form either pattern (starting with 0 or starting with 1), so we calculate swaps for both and take the minimum. When counts are unequal, only one pattern is possible - the one that starts with the more frequent character.
The function calc(c)
implements this by checking each position: if position i
should have character (c + i) % 2
but doesn't, we count it as a mismatch. The total mismatches divided by 2 gives us the swap count.
Learn more about Greedy patterns.
Solution Approach
The implementation follows a systematic approach to determine the minimum swaps needed:
Step 1: Count the characters
First, we count the occurrences of '0' and '1' in the string:
n0 = s.count("0")
n1 = len(s) - n0
Step 2: Check feasibility
We check if it's possible to create an alternating string by examining the difference between counts:
if abs(n0 - n1) > 1:
return -1
If the absolute difference is greater than 1, it's impossible to form an alternating string.
Step 3: Define the calculation function
The core logic is in the calc(c)
function, which calculates the number of swaps needed to form an alternating string starting with character c
:
def calc(c: int) -> int:
return sum((c ^ i & 1) != x for i, x in enumerate(map(int, s))) // 2
This function works by:
- Iterating through each position
i
and characterx
in the string - Using
(c ^ i & 1)
to determine what character should be at positioni
:- If
c = 0
andi
is even, orc = 1
andi
is odd: expects '0' - If
c = 0
andi
is odd, orc = 1
andi
is even: expects '1'
- If
- Counting mismatches where the actual character
x
doesn't match the expected character - Dividing by 2 since each swap fixes two mismatched positions
Step 4: Determine which pattern(s) to check
Based on the character counts:
-
Equal counts (
n0 == n1
): Both patterns are possibleif n0 == n1: return min(calc(0), calc(1))
We calculate swaps for both patterns (starting with 0 and starting with 1) and return the minimum.
-
Unequal counts: Only one pattern is possible
return calc(0 if n0 > n1 else 1)
We must start with the more frequent character. If there are more 0s, the pattern must be "010101...", otherwise it must be "101010...".
The algorithm has a time complexity of O(n)
where n
is the length of the string, as we traverse the string a constant number of times. The space complexity is O(1)
as we only use a few variables to store counts and results.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with the string s = "111000"
.
Step 1: Count characters
- Count of '0's:
n0 = 3
- Count of '1's:
n1 = 3
- String length:
n = 6
Step 2: Check feasibility
abs(3 - 3) = 0
, which is β€ 1, so it's possible to form an alternating string
Step 3: Determine possible patterns
Since n0 == n1
, we can form either pattern:
- Pattern A: "010101" (starting with 0)
- Pattern B: "101010" (starting with 1)
Step 4: Calculate swaps for Pattern A ("010101")
Let's trace through calc(0)
:
- Position 0: Should be '0', actual is '1' β mismatch β
- Position 1: Should be '1', actual is '1' β match β
- Position 2: Should be '0', actual is '1' β mismatch β
- Position 3: Should be '1', actual is '0' β mismatch β
- Position 4: Should be '0', actual is '0' β match β
- Position 5: Should be '1', actual is '0' β mismatch β
Total mismatches = 4 Swaps needed = 4 Γ· 2 = 2
We can verify: swap positions (0,3) and (2,5):
- "111000" β "011100" (after swapping positions 0 and 3)
- "011100" β "010101" (after swapping positions 2 and 5)
Step 5: Calculate swaps for Pattern B ("101010")
Let's trace through calc(1)
:
- Position 0: Should be '1', actual is '1' β match β
- Position 1: Should be '0', actual is '1' β mismatch β
- Position 2: Should be '1', actual is '1' β match β
- Position 3: Should be '0', actual is '0' β match β
- Position 4: Should be '1', actual is '0' β mismatch β
- Position 5: Should be '0', actual is '0' β match β
Total mismatches = 2 Swaps needed = 2 Γ· 2 = 1
We can verify: swap positions (1,4):
- "111000" β "101010" (after swapping positions 1 and 4)
Step 6: Return minimum
min(2, 1) = 1
The minimum number of swaps needed is 1.
Solution Implementation
1class Solution:
2 def minSwaps(self, s: str) -> int:
3 def calculate_swaps_for_pattern(starting_digit: int) -> int:
4 """
5 Calculate minimum swaps needed to match a specific alternating pattern.
6
7 Args:
8 starting_digit: 0 or 1, indicating whether pattern starts with '0' or '1'
9
10 Returns:
11 Number of swaps needed (each swap fixes 2 mismatched positions)
12 """
13 mismatches = 0
14 for position, current_char in enumerate(s):
15 # Expected digit at this position in alternating pattern
16 expected_digit = (starting_digit ^ position) & 1
17 # Convert current character to integer for comparison
18 actual_digit = int(current_char)
19 # Count if current position doesn't match expected pattern
20 if expected_digit != actual_digit:
21 mismatches += 1
22
23 # Each swap fixes 2 mismatched positions
24 return mismatches // 2
25
26 # Count occurrences of '0' and '1' in the string
27 count_zeros = s.count("0")
28 count_ones = len(s) - count_zeros
29
30 # Check if alternating pattern is possible
31 # For alternating pattern, difference between counts must be at most 1
32 if abs(count_zeros - count_ones) > 1:
33 return -1
34
35 # If counts are equal, try both patterns and return minimum
36 if count_zeros == count_ones:
37 swaps_start_with_0 = calculate_swaps_for_pattern(0)
38 swaps_start_with_1 = calculate_swaps_for_pattern(1)
39 return min(swaps_start_with_0, swaps_start_with_1)
40
41 # If counts differ by 1, only one pattern is valid
42 # The digit with more occurrences must start the pattern
43 if count_zeros > count_ones:
44 return calculate_swaps_for_pattern(0)
45 else:
46 return calculate_swaps_for_pattern(1)
47
1class Solution {
2 private char[] charArray;
3
4 /**
5 * Finds the minimum number of swaps needed to make a binary string alternating.
6 * An alternating string has no two adjacent characters that are the same.
7 *
8 * @param s The input binary string containing only '0' and '1'
9 * @return The minimum number of swaps needed, or -1 if impossible
10 */
11 public int minSwaps(String s) {
12 this.charArray = s.toCharArray();
13
14 // Count the number of 1s in the string
15 int onesCount = 0;
16 for (char c : this.charArray) {
17 onesCount += (c - '0');
18 }
19
20 // Calculate the number of 0s
21 int zerosCount = this.charArray.length - onesCount;
22
23 // Check if it's possible to make the string alternating
24 // The difference between count of 0s and 1s should be at most 1
25 if (Math.abs(zerosCount - onesCount) > 1) {
26 return -1;
27 }
28
29 // If counts are equal, we can start with either 0 or 1
30 if (zerosCount == onesCount) {
31 return Math.min(calculateSwaps(0), calculateSwaps(1));
32 }
33
34 // If counts are different, the more frequent digit must start first
35 return calculateSwaps(zerosCount > onesCount ? 0 : 1);
36 }
37
38 /**
39 * Calculates the number of swaps needed for a specific alternating pattern.
40 *
41 * @param startingDigit The digit (0 or 1) that should be at even positions
42 * @return The number of swaps needed
43 */
44 private int calculateSwaps(int startingDigit) {
45 int mismatchCount = 0;
46
47 for (int i = 0; i < charArray.length; ++i) {
48 int currentDigit = charArray[i] - '0';
49
50 // Determine what digit should be at position i
51 // Even positions should have startingDigit, odd positions should have opposite
52 // Using XOR: (i & 1) ^ startingDigit gives expected digit at position i
53 int expectedDigit = (i & 1) ^ startingDigit;
54
55 // Count mismatches
56 if (expectedDigit != currentDigit) {
57 ++mismatchCount;
58 }
59 }
60
61 // Each swap fixes two mismatches, so divide by 2
62 return mismatchCount / 2;
63 }
64}
65
1class Solution {
2public:
3 int minSwaps(string s) {
4 // Count the number of '0's and '1's in the string
5 int countZeros = 0;
6 for (char ch : s) {
7 if (ch == '0') {
8 countZeros++;
9 }
10 }
11 int countOnes = s.size() - countZeros;
12
13 // If the difference between counts is more than 1,
14 // it's impossible to create an alternating pattern
15 if (abs(countZeros - countOnes) > 1) {
16 return -1;
17 }
18
19 // Lambda function to calculate swaps needed for a specific pattern
20 // startChar: 0 means pattern starts with '0' (0101...), 1 means starts with '1' (1010...)
21 auto calculateSwaps = [&](int startChar) -> int {
22 int mismatchCount = 0;
23
24 // Check each position to see if it matches the expected pattern
25 for (int i = 0; i < s.size(); i++) {
26 int currentDigit = s[i] - '0';
27
28 // Expected digit at position i:
29 // If startChar = 0: even positions should be 0, odd positions should be 1
30 // If startChar = 1: even positions should be 1, odd positions should be 0
31 int expectedDigit = (i % 2) ^ startChar;
32
33 if (currentDigit != expectedDigit) {
34 mismatchCount++;
35 }
36 }
37
38 // Each swap fixes two mismatches, so divide by 2
39 return mismatchCount / 2;
40 };
41
42 // If counts are equal, we can start with either '0' or '1'
43 if (countZeros == countOnes) {
44 return min(calculateSwaps(0), calculateSwaps(1));
45 }
46
47 // If counts are unequal, the more frequent digit must be at even positions
48 // (since there are ceiling(n/2) even positions for a string of length n)
49 return calculateSwaps(countZeros > countOnes ? 0 : 1);
50 }
51};
52
1/**
2 * Calculates the minimum number of swaps needed to make a binary string alternating.
3 * An alternating string has no two adjacent characters that are the same.
4 *
5 * @param s - The input binary string containing only '0' and '1'
6 * @returns The minimum number of swaps needed, or -1 if impossible
7 */
8function minSwaps(s: string): number {
9 // Count the number of '0's and '1's in the string
10 const zeroCount: number = (s.match(/0/g) || []).length;
11 const oneCount: number = s.length - zeroCount;
12
13 // If the difference between counts is more than 1, it's impossible to make alternating
14 if (Math.abs(zeroCount - oneCount) > 1) {
15 return -1;
16 }
17
18 /**
19 * Calculates the number of swaps needed for a specific alternating pattern.
20 *
21 * @param startingChar - The character (0 or 1) that should appear at even indices
22 * @returns The number of swaps needed for this pattern
23 */
24 const calculateSwapsForPattern = (startingChar: number): number => {
25 let mismatchCount: number = 0;
26
27 // Check each position to see if it matches the expected pattern
28 for (let i = 0; i < s.length; i++) {
29 const currentChar: number = +s[i];
30
31 // XOR operation to determine expected character at position i
32 // If i is even and startingChar is 0, expect 0
33 // If i is odd and startingChar is 0, expect 1
34 // If i is even and startingChar is 1, expect 1
35 // If i is odd and startingChar is 1, expect 0
36 if (((i & 1) ^ startingChar) !== currentChar) {
37 mismatchCount++;
38 }
39 }
40
41 // Each swap fixes two mismatches, so divide by 2
42 return Math.floor(mismatchCount / 2);
43 };
44
45 // If counts are equal, try both patterns and return the minimum
46 if (zeroCount === oneCount) {
47 return Math.min(
48 calculateSwapsForPattern(0), // Pattern: 0101...
49 calculateSwapsForPattern(1) // Pattern: 1010...
50 );
51 }
52
53 // If counts are unequal, only one pattern is possible
54 // The more frequent character must start at index 0
55 return calculateSwapsForPattern(zeroCount > oneCount ? 0 : 1);
56}
57
Time and Space Complexity
Time Complexity: O(n)
, where n
is the length of the string s
.
The algorithm performs the following operations:
s.count("0")
iterates through the string once:O(n)
len(s) - n0
is a constant time operation:O(1)
- The
calc
function iterates through the string once usingenumerate(map(int, s))
and performs constant-time operations for each character:O(n)
- In the worst case (when
n0 == n1
), thecalc
function is called twice, but this is still2 * O(n) = O(n)
Therefore, the overall time complexity is O(n)
.
Space Complexity: O(1)
The algorithm uses only a constant amount of extra space:
- Variables
n0
andn1
store integer counts:O(1)
- The
calc
function creates an iterator withmap(int, s)
which doesn't create a new list but processes elements on-the-fly:O(1)
- The sum operation accumulates a single integer value:
O(1)
- No additional data structures that scale with input size are created
Therefore, the space complexity is O(1)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrectly Calculating the Number of Swaps
A frequent mistake is thinking that the number of swaps equals the total number of mismatched positions. This leads to returning mismatches
instead of mismatches // 2
.
Why this happens: When comparing the current string to the target alternating pattern, we count each position that doesn't match. However, a single swap operation fixes TWO mismatched positions simultaneously - one character moves to where it should be, and another character takes its place.
Incorrect approach:
def calculate_swaps_for_pattern(starting_digit: int) -> int:
mismatches = 0
for position, current_char in enumerate(s):
expected_digit = (starting_digit ^ position) & 1
if expected_digit != int(current_char):
mismatches += 1
return mismatches # WRONG: Returns total mismatches, not swaps
Correct approach:
def calculate_swaps_for_pattern(starting_digit: int) -> int:
mismatches = 0
for position, current_char in enumerate(s):
expected_digit = (starting_digit ^ position) & 1
if expected_digit != int(current_char):
mismatches += 1
return mismatches // 2 # CORRECT: Each swap fixes 2 positions
2. Misunderstanding Which Pattern to Check When Counts Are Unequal
When the counts of '0' and '1' differ by 1, only one alternating pattern is valid. A common error is checking the wrong pattern or always defaulting to one pattern.
Incorrect approach:
if count_zeros != count_ones: return calculate_swaps_for_pattern(0) # WRONG: Always starts with 0
Correct approach:
if count_zeros > count_ones: return calculate_swaps_for_pattern(0) # More 0s, must start with 0 else: return calculate_swaps_for_pattern(1) # More 1s, must start with 1
3. Confusion with the XOR Logic for Pattern Generation
The expression (starting_digit ^ position) & 1
can be confusing. Developers might incorrectly implement the alternating pattern check.
Common mistakes:
- Using
starting_digit + position
instead of XOR - Forgetting the
& 1
to get the least significant bit - Inverting the logic
Understanding the XOR pattern:
- When
starting_digit = 0
: positions 0,2,4... should have '0', positions 1,3,5... should have '1' - When
starting_digit = 1
: positions 0,2,4... should have '1', positions 1,3,5... should have '0'
Alternative clearer implementation:
def calculate_swaps_for_pattern(starting_digit: int) -> int:
mismatches = 0
for position, current_char in enumerate(s):
# More explicit pattern determination
if position % 2 == 0:
expected_digit = starting_digit
else:
expected_digit = 1 - starting_digit
if expected_digit != int(current_char):
mismatches += 1
return mismatches // 2
4. Not Handling Edge Cases Properly
Missing validation for impossible cases:
# Forgetting this check will cause incorrect results
if abs(count_zeros - count_ones) > 1:
return -1
Without this check, the algorithm might return a swap count for strings that cannot be made alternating, such as "0000" or "111".
Depth first search is equivalent to which of the tree traversal order?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Donβt Miss This!