Taro Logo

Split a String in Balanced Strings

Easy
a month ago

Balanced strings are those that have an equal quantity of 'L' and 'R' characters.

Given a balanced string s, split it into some number of substrings such that:

  • Each substring is balanced.

Return the maximum number of balanced strings you can obtain.

For example:

Input: s = "RLRRLLRLRL" Output: 4 Explanation: s can be split into "RL", "RRLL", "RL", "RL", each substring contains same number of 'L' and 'R'.

Input: s = "RLRRRLLRLL" Output: 2 Explanation: s can be split into "RL", "RRRLLRLL", each substring contains same number of 'L' and 'R'. Note that s cannot be split into "RL", "RR", "RL", "LR", "LL", because the 2nd and 5th substrings are not balanced.

Input: s = "LLLLRRRR" Output: 1 Explanation: s can be split into "LLLLRRRR".

Sample Answer
def balancedStringSplit(s):
    """Splits a balanced string into the maximum number of balanced substrings."""
    balance = 0
    count = 0
    for char in s:
        if char == 'R':
            balance += 1
        else:
            balance -= 1
        if balance == 0:
            count += 1
    return count

# Example Usage
s = "RLRRLLRLRL"
result = balancedStringSplit(s)
print(f"The maximum number of balanced strings is: {result}") # Output: 4

s = "RLRRRLLRLL"
result = balancedStringSplit(s)
print(f"The maximum number of balanced strings is: {result}") # Output: 2

s = "LLLLRRRR"
result = balancedStringSplit(s)
print(f"The maximum number of balanced strings is: {result}") # Output: 1

Explanation:

  1. Initialization:

    • balance: Keeps track of the balance between 'R' and 'L' characters. It starts at 0.
    • count: Stores the number of balanced substrings. It starts at 0.
  2. Iteration:

    • The code iterates through each character in the input string s.
    • If the character is 'R', the balance is incremented by 1. If the character is 'L', the balance is decremented by 1.
    • If balance becomes 0, it means the substring encountered so far is balanced (equal number of 'R' and 'L'). Therefore, the count is incremented.
  3. Return:

    • Finally, the function returns the count, which represents the maximum number of balanced substrings.

Naive Approach:

A naive approach would involve checking all possible substrings to see if they are balanced. This would have a time complexity of O(n^2), where n is the length of the string.

def balancedStringSplit_naive(s):
    n = len(s)
    count = 0
    for i in range(n):
        for j in range(i + 1, n + 1):
            sub = s[i:j]
            r_count = sub.count('R')
            l_count = sub.count('L')
            if r_count == l_count and len(sub) > 0 and len(sub) % 2 == 0:
                count += 1
    return count

The optimized approach avoids unnecessary substring creation and counting by keeping track of the balance as we iterate through the string. It has a time complexity of O(n).

Big(O) Runtime Analysis:

The optimized solution iterates through the string s once. The operations inside the loop (incrementing/decrementing balance and checking if balance is 0) take constant time, O(1).

Therefore, the overall time complexity is O(n), where n is the length of the string s.

Big(O) Space Usage Analysis:

The solution uses only a few integer variables (balance and count). The amount of memory used does not depend on the size of the input string.

Therefore, the space complexity is O(1), which means constant space.

Edge Cases:

  1. Empty String: The problem statement specifies that the string will always be balanced and of length at least 2. So, an empty string is not an edge case to consider.
  2. String with Only 'L' or Only 'R': The problem statement specifies that the string is balanced, so this edge case is automatically excluded.
  3. Very Long String: The code should handle very long strings efficiently because the time complexity is O(n). There are no recursive calls or large data structures that would cause a stack overflow or memory issues.
  4. Invalid Characters: Input string is guaranteed to consist of only 'L' and 'R'.