Facebook Pixel

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:

  1. Starting with '0': "0101010..."
  2. 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).

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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 exactly n/2 zeros and n/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:

  1. Count how many positions have the wrong character compared to the target pattern
  2. 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 character x in the string
  • Using (c ^ i & 1) to determine what character should be at position i:
    • If c = 0 and i is even, or c = 1 and i is odd: expects '0'
    • If c = 0 and i is odd, or c = 1 and i is even: expects '1'
  • 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 possible

    if 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 Evaluator

Example 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 using enumerate(map(int, s)) and performs constant-time operations for each character: O(n)
  • In the worst case (when n0 == n1), the calc function is called twice, but this is still 2 * 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 and n1 store integer counts: O(1)
  • The calc function creates an iterator with map(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".

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Depth first search is equivalent to which of the tree traversal order?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More