1758. Minimum Changes To Make Alternating Binary String
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.
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:
-
Count mismatches with Pattern 1: We iterate through each character in the string along with its index using
enumerate(s)
. For each positioni
, we check if the characterc
matches what should be at that position in Pattern 1. -
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..."
- When
-
Count differences: The expression
c != '01'[i & 1]
returnsTrue
(counted as 1) when the character doesn't match Pattern 1, andFalse
(counted as 0) when it matches. Thesum()
function adds up all the mismatches, giving uscnt
. -
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. - Converting to Pattern 1 requires
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 EvaluatorExample 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 s | Expected in Pattern 1 | i & 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)
Which algorithm should you use to find a node that is close to the root of the tree?
Recommended Readings
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
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!