Facebook Pixel

1422. Maximum Score After Splitting a String

Problem Description

You are given a string s consisting only of zeros ('0') and ones ('1'). Your task is to split this string into two non-empty parts (a left substring and a right substring) and calculate the maximum possible score.

The score is calculated as follows:

  • Count the number of zeros in the left substring
  • Count the number of ones in the right substring
  • Add these two counts together to get the score

You need to find the maximum score possible among all valid ways to split the string.

For example, if s = "011101":

  • If you split after the first character: left = "0", right = "11101"
    • Score = 1 zero in left + 4 ones in right = 5
  • If you split after the second character: left = "01", right = "1101"
    • Score = 1 zero in left + 3 ones in right = 4
  • If you split after the third character: left = "011", right = "101"
    • Score = 1 zero in left + 2 ones in right = 3

The constraint is that both parts must be non-empty, meaning you cannot take the entire string as left (leaving right empty) or take the entire string as right (leaving left empty).

Your goal is to return the maximum score achievable.

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

Intuition

The key insight is that we need to try every possible split position and find which one gives us the maximum score. Since we're splitting the string into two parts, we have n-1 possible positions to split (where n is the length of the string) - we can split after the 1st character, 2nd character, and so on, up to the second-to-last character.

A naive approach would be to actually split the string at each position and count zeros and ones in each part, but this would be inefficient. Instead, we can think about what happens as we move the split position from left to right:

  • Initially, if we split after the first character, we have one character on the left and all remaining characters on the right
  • As we move the split position one step to the right, we're essentially moving one character from the right substring to the left substring

This movement has a predictable effect on our score:

  • If the character we're moving is a '0', it increases the left count (good for score) and doesn't affect the right count of ones
  • If the character we're moving is a '1', it doesn't help the left count but decreases the right count of ones (bad for score)

So we can start by counting all the '1's in the string (this would be our right count if we split after the first character with an empty left count initially). Then, as we iterate through each potential split position:

  • For each '0' we encounter, we increment our left count l by 1
  • For each '1' we encounter, we decrement our right count r by 1
  • At each position, we calculate the score as l + r and track the maximum

The expression int(x) ^ 1 is a clever way to count zeros: it converts '0' to 0 and '1' to 1, then XOR with 1 flips the bit (0 becomes 1, 1 becomes 0), effectively counting zeros instead of ones.

Learn more about Prefix Sum patterns.

Solution Approach

The solution implements a single-pass counting approach to find the maximum score efficiently.

Initialization:

  • l = 0: Tracks the count of zeros in the left substring (initially empty)
  • r = s.count("1"): Counts all ones in the string (initially all characters are in the right substring)
  • ans = 0: Stores the maximum score found so far

Main Algorithm:

We iterate through the string from index 0 to n-2 (using s[:-1] to exclude the last character). This ensures both substrings remain non-empty after the split.

For each character x at position i:

  1. Update left count: l += int(x) ^ 1

    • int(x) converts '0' to 0 and '1' to 1
    • XOR with 1 (^ 1) flips the bit: 0 becomes 1, 1 becomes 0
    • This effectively adds 1 to l if the character is '0', and adds 0 if it's '1'
  2. Update right count: r -= int(x)

    • Subtracts 1 from r if the character is '1'
    • Subtracts 0 from r if the character is '0'
    • This removes the ones that have moved from right to left substring
  3. Update maximum score: ans = max(ans, l + r)

    • Calculate the current score as the sum of zeros in left and ones in right
    • Keep track of the maximum score seen so far

Why this works:

At each iteration, we're conceptually moving the split position one character to the right. The character at position i moves from the right substring to the left substring. The counts are adjusted accordingly, and we check if this new split gives us a better score.

Time Complexity: O(n) where n is the length of the string - we make one initial pass to count ones and one main pass through the string.

Space Complexity: O(1) - we only use a constant amount of extra space for variables.

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 s = "00111":

Initial Setup:

  • l = 0 (no zeros counted in left substring yet)
  • r = s.count("1") = 3 (all three ones are initially in the "right" side)
  • ans = 0 (maximum score so far)

Iteration 1: Character at index 0 is '0'

  • Split would be: left = "0" | right = "0111"
  • l += int('0') ^ 1 = 0 ^ 1 = 1, so l = 1
  • r -= int('0') = 0, so r = 3 (no ones moved out)
  • Current score = l + r = 1 + 3 = 4
  • ans = max(0, 4) = 4

Iteration 2: Character at index 1 is '0'

  • Split would be: left = "00" | right = "111"
  • l += int('0') ^ 1 = 0 ^ 1 = 1, so l = 2
  • r -= int('0') = 0, so r = 3 (no ones moved out)
  • Current score = l + r = 2 + 3 = 5
  • ans = max(4, 5) = 5

Iteration 3: Character at index 2 is '1'

  • Split would be: left = "001" | right = "11"
  • l += int('1') ^ 1 = 1 ^ 1 = 0, so l = 2 (no change, not a zero)
  • r -= int('1') = 1, so r = 2 (one '1' moved to left side)
  • Current score = l + r = 2 + 2 = 4
  • ans = max(5, 4) = 5

Iteration 4: Character at index 3 is '1'

  • Split would be: left = "0011" | right = "1"
  • l += int('1') ^ 1 = 1 ^ 1 = 0, so l = 2 (no change, not a zero)
  • r -= int('1') = 1, so r = 1 (another '1' moved to left side)
  • Current score = l + r = 2 + 1 = 3
  • ans = max(5, 3) = 5

Result: The maximum score is 5, achieved when splitting after the second character ("00" | "111").

Solution Implementation

1class Solution:
2    def maxScore(self, s: str) -> int:
3        # Initialize counters for left and right parts
4        # left_zeros: count of zeros in the left substring (starts at 0)
5        # right_ones: count of ones in the right substring (initially all ones)
6        left_zeros = 0
7        right_ones = s.count("1")
8      
9        # Track the maximum score found
10        max_score = 0
11      
12        # Iterate through all possible split positions (except the last character
13        # to ensure right substring is non-empty)
14        for i in range(len(s) - 1):
15            char = s[i]
16          
17            # Update counters based on current character
18            if char == "0":
19                # Add a zero to the left part
20                left_zeros += 1
21            else:
22                # Move a one from right part to left part
23                right_ones -= 1
24          
25            # Calculate current score and update maximum
26            current_score = left_zeros + right_ones
27            max_score = max(max_score, current_score)
28      
29        return max_score
30
1class Solution {
2    public int maxScore(String s) {
3        // Initialize counters for left substring (zeros) and right substring (ones)
4        int leftZeros = 0;
5        int rightOnes = 0;
6        int length = s.length();
7      
8        // Count total number of ones in the entire string
9        // This will be the initial count for the right substring
10        for (int i = 0; i < length; ++i) {
11            if (s.charAt(i) == '1') {
12                ++rightOnes;
13            }
14        }
15      
16        // Variable to store the maximum score
17        int maxScore = 0;
18      
19        // Iterate through possible split positions (cannot split at the last position)
20        // Split position i means: left substring [0, i], right substring [i+1, n-1]
21        for (int i = 0; i < length - 1; ++i) {
22            // Update left zeros count
23            // If current character is '0', increment leftZeros
24            // (s.charAt(i) - '0') ^ 1 converts '0' to 1 and '1' to 0
25            leftZeros += (s.charAt(i) - '0') ^ 1;
26          
27            // Update right ones count
28            // If current character is '1', decrement rightOnes
29            // since this '1' is now part of the left substring
30            rightOnes -= s.charAt(i) - '0';
31          
32            // Calculate current score and update maximum
33            maxScore = Math.max(maxScore, leftZeros + rightOnes);
34        }
35      
36        return maxScore;
37    }
38}
39
1class Solution {
2public:
3    int maxScore(string s) {
4        // Count zeros in left substring (initially empty)
5        int leftZeros = 0;
6      
7        // Count all ones in the string (initially all in right substring)
8        int rightOnes = count(s.begin(), s.end(), '1');
9      
10        // Track maximum score
11        int maxScore = 0;
12      
13        // Iterate through possible split positions (exclude last position)
14        // We stop at s.size() - 1 because both substrings must be non-empty
15        for (int i = 0; i < s.size() - 1; ++i) {
16            // Update left zeros count
17            // If s[i] is '0', increment leftZeros (XOR with 1 flips 0 to 1)
18            leftZeros += (s[i] - '0') ^ 1;
19          
20            // Update right ones count
21            // If s[i] is '1', decrement rightOnes (moving it from right to left)
22            rightOnes -= s[i] - '0';
23          
24            // Calculate current score and update maximum
25            maxScore = max(maxScore, leftZeros + rightOnes);
26        }
27      
28        return maxScore;
29    }
30};
31
1/**
2 * Calculates the maximum score by splitting a binary string into two non-empty substrings.
3 * Score = number of '0's in left substring + number of '1's in right substring
4 * @param s - Binary string containing only '0' and '1' characters
5 * @returns Maximum possible score
6 */
7function maxScore(s: string): number {
8    // Initialize counters for zeros in left part and ones in right part
9    let zerosInLeft: number = 0;
10    let onesInRight: number = 0;
11  
12    // Count total number of '1's in the entire string (initially all in right part)
13    for (const char of s) {
14        if (char === '1') {
15            onesInRight++;
16        }
17    }
18  
19    let maxScoreValue: number = 0;
20  
21    // Iterate through possible split positions (cannot split at the last position)
22    for (let i = 0; i < s.length - 1; i++) {
23        // Update counters based on current character moving from right to left part
24        if (s[i] === '0') {
25            // Found a '0', increase left counter
26            zerosInLeft++;
27        } else {
28            // Found a '1', decrease right counter as it moves to left part
29            onesInRight--;
30        }
31      
32        // Calculate current score and update maximum
33        const currentScore: number = zerosInLeft + onesInRight;
34        maxScoreValue = Math.max(maxScoreValue, currentScore);
35    }
36  
37    return maxScoreValue;
38}
39

Time and Space Complexity

The time complexity is O(n), where n is the length of the string s. The algorithm performs the following operations:

  • s.count("1") traverses the entire string once: O(n)
  • The for loop iterates through n-1 characters of the string: O(n-1) = O(n)
  • Each operation inside the loop (XOR, addition, subtraction, max comparison) takes constant time: O(1)

Therefore, the overall time complexity is O(n) + O(n) = O(n).

The space complexity is O(1). The algorithm only uses a fixed number of variables (l, r, ans, and x) regardless of the input size, requiring constant extra space.

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

Common Pitfalls

1. Forgetting to Handle Edge Cases with String Boundaries

A critical pitfall is iterating through the entire string instead of stopping at len(s) - 1. This would result in considering a split where the right substring is empty, which violates the problem constraint.

Incorrect Implementation:

# WRONG: This iterates through all characters including the last one
for i in range(len(s)):  # Bug: includes index len(s)-1
    char = s[i]
    # ... rest of the logic

Why it's wrong: When i = len(s) - 1, the split would be:

  • Left substring: entire string s
  • Right substring: empty string ""

This violates the "non-empty" constraint for both parts.

Correct Implementation:

# CORRECT: Stop before the last character
for i in range(len(s) - 1):  # Ensures right substring is never empty
    char = s[i]
    # ... rest of the logic

2. Incorrect Initial Score Calculation

Another common mistake is initializing max_score with the wrong value or forgetting that we need to actually check splits, not just initialize with a theoretical value.

Incorrect Implementation:

# WRONG: Assuming the initial state is a valid split
left_zeros = 0
right_ones = s.count("1")
max_score = left_zeros + right_ones  # Bug: This represents no split at all

Why it's wrong: The initial values left_zeros = 0 and right_ones = s.count("1") don't represent any actual split of the string - they represent a state where nothing is in the left substring.

Correct Implementation:

# CORRECT: Initialize max_score to 0 and let the loop find the actual maximum
max_score = 0
# The first iteration will set a valid initial score

3. Off-by-One Error in Character Movement Logic

A subtle pitfall is incorrectly updating the counters when moving a character from right to left.

Incorrect Implementation:

# WRONG: Decrementing right_ones for zeros too
for i in range(len(s) - 1):
    char = s[i]
    if char == "0":
        left_zeros += 1
        right_ones -= 1  # Bug: zeros don't affect right_ones count
    else:
        right_ones -= 1

Correct Implementation:

# CORRECT: Only decrement right_ones when moving a '1' from right to left
for i in range(len(s) - 1):
    char = s[i]
    if char == "0":
        left_zeros += 1
        # No change to right_ones (zeros don't count in right substring)
    else:
        right_ones -= 1  # Only decrease when moving a '1'
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the following is a min heap?


Recommended Readings

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

Load More