Facebook Pixel

1758. Minimum Changes To Make Alternating Binary String

EasyString
Leetcode Link

Problem Description

You have a binary string s containing only '0's and '1's. Your goal is to transform this string into an alternating pattern where no two adjacent characters are the same.

An alternating string means that each character is different from its neighbors. For instance:

  • "010" is alternating (each character differs from the ones next to it)
  • "0100" is not alternating (the middle two characters are both '0')

You can perform operations to change any character:

  • Change a '0' to '1'
  • Change a '1' to '0'

Each change counts as one operation.

The task is to find the minimum number of operations needed to make the string alternating.

For example, if you have "0100", you could change it to either "0101" or "1010" to make it alternating. The first option requires 1 operation (changing the third character), while the second requires 2 operations (changing the first and third characters). So the minimum would be 1.

Since there are only two possible alternating patterns that a string can follow (starting with '0' or starting with '1'), the solution involves checking both patterns and choosing the one that requires fewer changes.

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

Intuition

When we think about alternating strings, we realize there are only two possible patterns:

  • Pattern 1: "0101010..." (starts with '0')
  • Pattern 2: "1010101..." (starts with '1')

Any alternating string must follow one of these two patterns. This is the key insight.

Now, to transform our string s into an alternating string, we need to choose which pattern requires fewer changes. Let's think about this systematically:

If we count how many positions in s don't match Pattern 1, let's call this count cnt. These are the positions we'd need to change if we wanted to follow Pattern 1.

Here's the clever observation: if a character at position i doesn't match Pattern 1, then it automatically matches Pattern 2! Why? Because Pattern 2 is exactly the opposite of Pattern 1 at every position.

This means:

  • Operations needed for Pattern 1 = cnt
  • Operations needed for Pattern 2 = len(s) - cnt

Therefore, the minimum operations needed is min(cnt, len(s) - cnt).

In the code, Pattern 1 is generated using '01'[i & 1], which alternates between '0' and '1' based on whether the index i is even or odd. The expression i & 1 gives 0 for even indices and 1 for odd indices, perfectly generating the "0101..." pattern.

Solution Approach

The implementation follows the intuition directly with an elegant one-liner approach:

  1. Count mismatches with Pattern 1: We iterate through each character in the string along with its index using enumerate(s). For each position i, we check if the character c matches what should be at that position in Pattern 1.

  2. Generate Pattern 1 dynamically: Instead of creating the entire pattern string, we generate each expected character on-the-fly using '01'[i & 1]:

    • When i is even: i & 1 = 0, so we get '01'[0] = '0'
    • When i is odd: i & 1 = 1, so we get '01'[1] = '1'
    • This produces the pattern "0101..."
  3. Count differences: The expression c != '01'[i & 1] returns True (counted as 1) when the character doesn't match Pattern 1, and False (counted as 0) when it matches. The sum() function adds up all the mismatches, giving us cnt.

  4. Find minimum: Since we know:

    • Converting to Pattern 1 requires cnt operations
    • Converting to Pattern 2 requires len(s) - cnt operations

    We return min(cnt, len(s) - cnt) as the minimum number of operations needed.

The beauty of this solution is its efficiency - it makes only one pass through the string (O(n) time complexity) and uses constant extra space (O(1) space complexity), while leveraging the complementary nature of the two possible patterns.

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 = "0100".

Step 1: Identify the two possible alternating patterns

  • Pattern 1: "0101" (starts with '0')
  • Pattern 2: "1010" (starts with '1')

Step 2: Count mismatches with Pattern 1

Let's iterate through each character and check if it matches Pattern 1:

Index (i)Character in sExpected in Pattern 1i & 1'01'[i & 1]Match?
0'0''0'0'0'✓ Yes
1'1''1'1'1'✓ Yes
2'0''0'0'0'✓ Yes
3'0''1'1'1'✗ No

Count of mismatches with Pattern 1: cnt = 1

Step 3: Calculate operations needed for each pattern

  • Operations for Pattern 1: cnt = 1 (change position 3 from '0' to '1')
  • Operations for Pattern 2: len(s) - cnt = 4 - 1 = 3 (change positions 0, 1, and 2)

Step 4: Find the minimum min(1, 3) = 1

Therefore, the minimum number of operations is 1.

To verify: changing "0100" → "0101" requires 1 operation (changing the last character), which gives us an alternating string.

Solution Implementation

1class Solution:
2    def minOperations(self, s: str) -> int:
3        # Count operations needed to match pattern starting with '0'
4        # Pattern: '0' at even indices, '1' at odd indices (0101...)
5        operations_start_with_zero = 0
6      
7        for index, char in enumerate(s):
8            # Determine expected character based on index parity
9            # Even index -> '0', Odd index -> '1'
10            expected_char = '01'[index & 1]  # Bitwise AND with 1 gives 0 for even, 1 for odd
11          
12            # Count mismatch if current character doesn't match expected
13            if char != expected_char:
14                operations_start_with_zero += 1
15      
16        # The other pattern (starting with '1') would need the remaining operations
17        # Total operations for pattern '1010...' = len(s) - operations for '0101...'
18        operations_start_with_one = len(s) - operations_start_with_zero
19      
20        # Return minimum operations needed for either pattern
21        return min(operations_start_with_zero, operations_start_with_one)
22
1class Solution {
2    public int minOperations(String s) {
3        // Count operations needed to make string start with '0' (pattern: 010101...)
4        int operationsForPattern0 = 0;
5        int stringLength = s.length();
6      
7        // Check each character against the expected pattern starting with '0'
8        for (int i = 0; i < stringLength; i++) {
9            // For pattern starting with '0':
10            // - Even indices (0, 2, 4, ...) should have '0'
11            // - Odd indices (1, 3, 5, ...) should have '1'
12            // Using bitwise AND: i & 1 gives 0 for even indices, 1 for odd indices
13            char expectedChar = "01".charAt(i & 1);
14          
15            // If current character doesn't match expected, we need an operation
16            if (s.charAt(i) != expectedChar) {
17                operationsForPattern0++;
18            }
19        }
20      
21        // The other pattern (starting with '1') would need (stringLength - operationsForPattern0) operations
22        // Return the minimum of the two patterns
23        return Math.min(operationsForPattern0, stringLength - operationsForPattern0);
24    }
25}
26
1class Solution {
2public:
3    int minOperations(string s) {
4        // Count operations needed to match pattern "010101..."
5        int operationsForPattern1 = 0;
6        int n = s.size();
7      
8        // Check each position against the alternating pattern starting with '0'
9        // At even indices (0, 2, 4...), we expect '0'
10        // At odd indices (1, 3, 5...), we expect '1'
11        for (int i = 0; i < n; ++i) {
12            // i & 1 gives 0 for even indices, 1 for odd indices
13            // "01"[i & 1] gives '0' for even positions, '1' for odd positions
14            if (s[i] != "01"[i & 1]) {
15                operationsForPattern1++;
16            }
17        }
18      
19        // The other pattern "101010..." would need (n - operationsForPattern1) operations
20        // Return the minimum of the two possibilities
21        return min(operationsForPattern1, n - operationsForPattern1);
22    }
23};
24
1/**
2 * Calculates the minimum number of operations to make a binary string alternating.
3 * An alternating string is either "010101..." or "101010..."
4 * 
5 * @param s - The input binary string containing only '0' and '1'
6 * @returns The minimum number of operations needed to make the string alternating
7 */
8function minOperations(s: string): number {
9    const stringLength: number = s.length;
10  
11    // Count operations needed to match pattern "010101..."
12    let operationsForPattern01: number = 0;
13  
14    for (let index = 0; index < stringLength; index++) {
15        // Check if current character matches the expected pattern "010101..."
16        // When index is even (0, 2, 4...), expect '0'
17        // When index is odd (1, 3, 5...), expect '1'
18        const expectedChar: string = '01'[index & 1]; // Bitwise AND to determine even/odd
19      
20        if (s[index] !== expectedChar) {
21            operationsForPattern01++;
22        }
23    }
24  
25    // Operations for pattern "101010..." is the complement
26    const operationsForPattern10: number = stringLength - operationsForPattern01;
27  
28    // Return the minimum of the two patterns
29    return Math.min(operationsForPattern01, operationsForPattern10);
30}
31

Time and Space Complexity

Time Complexity: O(n), where n is the length of the string s. The algorithm iterates through the string once using enumerate() and performs a constant-time comparison and modulo operation for each character. The sum() function processes all elements in the generator expression, resulting in linear time complexity.

Space Complexity: O(1). The algorithm uses only a constant amount of extra space. The variable cnt stores a single integer value, and the generator expression (c != '01'[i & 1] for i, c in enumerate(s)) doesn't create an intermediate list but rather generates values on-the-fly. The bitwise operation i & 1 and string indexing '01'[i & 1] both use constant space.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Incorrect Pattern Generation Using Modulo

A common mistake is using regular modulo operator % instead of bitwise AND & for pattern generation, or misunderstanding how the pattern indices work:

Incorrect approach:

expected_char = str(index % 2)  # Returns '0' or '1' as string
if char != expected_char:  # Bug: comparing '0'/'1' with 0/1
    operations_start_with_zero += 1

Why it fails: The modulo operation returns an integer (0 or 1), not a character. Comparing string '0' with integer 0 will always return False.

Correct approach:

expected_char = '01'[index % 2]  # Or '01'[index & 1]
# This properly indexes into the string '01' to get a character

2. Forgetting the Complementary Relationship

Some developers try to count operations for both patterns independently:

Inefficient approach:

# Counting both patterns separately - unnecessary work!
ops_pattern1 = sum(1 for i, c in enumerate(s) if c != '01'[i & 1])
ops_pattern2 = sum(1 for i, c in enumerate(s) if c != '10'[i & 1])
return min(ops_pattern1, ops_pattern2)

Why it's inefficient: This makes two passes through the string when one is sufficient. Since the patterns are complementary, if a character matches Pattern 1, it won't match Pattern 2, and vice versa.

Optimal approach:

cnt = sum(c != '01'[i & 1] for i, c in enumerate(s))
return min(cnt, len(s) - cnt)

3. Off-by-One Errors in Pattern Assignment

Confusion about which pattern starts with which character:

Common mistake:

# Thinking "Pattern 1" must start with '1'
expected_char = '10'[index & 1]  # Wrong pattern assignment

Solution: Be consistent with your pattern definition. If you define Pattern 1 as starting with '0', stick with '01'[index & 1]. The actual starting character doesn't matter as long as you're consistent and check both possibilities.

4. Not Handling Edge Cases

While the problem typically guarantees non-empty strings, defensive programming suggests checking:

Robust version:

def minOperations(self, s: str) -> int:
    if not s or len(s) == 1:
        return 0  # Empty or single character is already alternating
  
    cnt = sum(c != '01'[i & 1] for i, c in enumerate(s))
    return min(cnt, len(s) - cnt)
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which algorithm should you use to find a node that is close to the root of the tree?


Recommended Readings

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

Load More