Facebook Pixel

1704. Determine if String Halves Are Alike

EasyStringCounting
Leetcode Link

Problem Description

You are given a string s that has an even length. Your task is to split this string into two equal halves and determine if these halves are "alike."

To split the string:

  • The first half a consists of the first n/2 characters
  • The second half b consists of the last n/2 characters

Two strings are considered alike if they contain the same number of vowels. The vowels to count are: 'a', 'e', 'i', 'o', 'u' and their uppercase versions 'A', 'E', 'I', 'O', 'U'.

The solution uses a clever counting approach:

  • It iterates through the first half of the string (first n characters where n = len(s) / 2)
  • For each position i in the first half, it simultaneously checks:
    • The character at position i (in the first half)
    • The character at position i + n (in the second half)
  • It maintains a counter cnt that:
    • Increases by 1 when a vowel is found in the first half
    • Decreases by 1 when a vowel is found in the second half
  • If cnt equals 0 at the end, both halves have the same number of vowels, so the function returns true

For example, if s = "book":

  • First half: "bo" (contains 1 vowel: 'o')
  • Second half: "ok" (contains 1 vowel: 'o')
  • Since both halves have 1 vowel, they are alike, and the function returns true
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 compare the vowel counts of two halves. The straightforward approach would be to count vowels in each half separately and then compare the counts. However, we can optimize this with a single-pass solution using a difference counter.

Instead of maintaining two separate counts, we can use a single counter that tracks the difference between vowel counts in the two halves. This works because:

  • If the first half has x vowels and the second half has y vowels
  • We want to check if x == y, which is equivalent to checking if x - y == 0

By iterating through the first half of the string, we can access both halves simultaneously:

  • For index i in the first half, we access character s[i]
  • The corresponding character in the second half is at index i + n (where n is half the string length)

This parallel processing allows us to:

  • Add 1 to our counter when we find a vowel in the first half
  • Subtract 1 from our counter when we find a vowel in the second half

If the counter ends at 0, it means both halves had the same number of vowels (all additions and subtractions balanced out). This elegant approach reduces the problem to a simple counting mechanism that processes both halves in a single loop, making the code both efficient and concise.

The use of a set for vowels (vowels = set('aeiouAEIOU')) provides O(1) lookup time for checking if a character is a vowel, making the overall solution run in O(n) time with O(1) extra space.

Solution Approach

The solution implements a counting approach to determine if both halves of the string have equal vowel counts.

Step-by-step implementation:

  1. Initialize variables:

    • cnt = 0: A counter to track the difference in vowel counts between the two halves
    • n = len(s) >> 1: Calculate half the length of the string (using bit shift for division by 2)
    • vowels = set('aeiouAEIOU'): Create a set containing all vowels for O(1) lookup
  2. Single-pass iteration:

    for i in range(n):
        cnt += s[i] in vowels
        cnt -= s[i + n] in vowels
    • Loop through indices from 0 to n-1 (first half of the string)
    • For each index i:
      • Check if s[i] (character in first half) is a vowel
        • If yes, s[i] in vowels returns True (which equals 1), so cnt increases by 1
        • If no, it returns False (which equals 0), so cnt remains unchanged
      • Check if s[i + n] (corresponding character in second half) is a vowel
        • If yes, subtract 1 from cnt
        • If no, subtract 0 (no change)
  3. Return the result:

    • return cnt == 0: If the counter is 0, both halves have the same vowel count

Why this works:

  • The algorithm leverages Python's boolean-to-integer conversion where True equals 1 and False equals 0
  • By processing both halves simultaneously and maintaining a difference counter, we avoid storing separate counts
  • The final check cnt == 0 confirms that all vowels in the first half were balanced by vowels in the second half

Time Complexity: O(n) where n is the length of the string (we iterate through half the string once)
Space Complexity: O(1) as we only use a fixed amount of extra space for the vowel set and counter

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

Step 1: Initialize variables

  • String length = 8, so n = 8 >> 1 = 4
  • First half: "text" (indices 0-3)
  • Second half: "book" (indices 4-7)
  • cnt = 0
  • vowels = {'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'}

Step 2: Iterate through first half

is[i]s[i+n]s[i] in vowelss[i+n] in vowelscnt calculationcnt value
0't''b'False (0)False (0)0 + 0 - 00
1'e''o'True (1)True (1)0 + 1 - 10
2'x''o'False (0)True (1)0 + 0 - 1-1
3't''k'False (0)False (0)-1 + 0 - 0-1

Step 3: Check result

  • Final cnt = -1
  • Since cnt ≠ 0, return False

Verification:

  • First half "text" has 1 vowel: 'e'
  • Second half "book" has 2 vowels: 'o', 'o'
  • 1 ≠ 2, so the halves are not alike ✓

The counter being -1 indicates the second half has one more vowel than the first half, which matches our manual count.

Solution Implementation

1class Solution:
2    def halvesAreAlike(self, s: str) -> bool:
3        """
4        Check if two halves of a string contain the same number of vowels.
5      
6        Args:
7            s: Input string to be split and checked
8          
9        Returns:
10            True if both halves have equal vowel count, False otherwise
11        """
12        # Initialize counter and calculate half length of string
13        vowel_count_difference = 0
14        half_length = len(s) // 2
15      
16        # Define set of vowels (both lowercase and uppercase)
17        vowels = {'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'}
18      
19        # Iterate through first half of string
20        for i in range(half_length):
21            # Increment counter if character in first half is a vowel
22            if s[i] in vowels:
23                vowel_count_difference += 1
24          
25            # Decrement counter if corresponding character in second half is a vowel
26            if s[i + half_length] in vowels:
27                vowel_count_difference -= 1
28      
29        # Return True if vowel counts are equal (difference is 0)
30        return vowel_count_difference == 0
31
1class Solution {
2    /**
3     * Determines if two halves of a string contain the same number of vowels.
4     * The string is split into two equal halves and vowel counts are compared.
5     * 
6     * @param s the input string (guaranteed to have even length)
7     * @return true if both halves have equal vowel counts, false otherwise
8     */
9    public boolean halvesAreAlike(String s) {
10        // Define set of vowels (both lowercase and uppercase)
11        Set<Character> vowels = Set.of('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U');
12      
13        // Calculate the midpoint of the string (half length)
14        int halfLength = s.length() / 2;
15      
16        // Counter to track the difference in vowel counts between halves
17        // Positive value means first half has more vowels
18        // Negative value means second half has more vowels
19        int vowelCountDifference = 0;
20      
21        // Iterate through the first half of the string
22        for (int i = 0; i < halfLength; i++) {
23            // Increment counter if character in first half is a vowel
24            if (vowels.contains(s.charAt(i))) {
25                vowelCountDifference++;
26            }
27          
28            // Decrement counter if corresponding character in second half is a vowel
29            if (vowels.contains(s.charAt(i + halfLength))) {
30                vowelCountDifference--;
31            }
32        }
33      
34        // Return true if both halves have equal vowel counts
35        return vowelCountDifference == 0;
36    }
37}
38
1class Solution {
2public:
3    bool halvesAreAlike(string s) {
4        // Define set of vowels (both lowercase and uppercase)
5        unordered_set<char> vowelSet = {'a', 'e', 'i', 'o', 'u', 
6                                         'A', 'E', 'I', 'O', 'U'};
7      
8        // Initialize counter for vowel difference between halves
9        int vowelDifference = 0;
10      
11        // Calculate half length of the string
12        int halfLength = s.size() / 2;
13      
14        // Iterate through the first half of the string
15        for (int i = 0; i < halfLength; ++i) {
16            // Add 1 if character in first half is a vowel
17            vowelDifference += vowelSet.count(s[i]);
18          
19            // Subtract 1 if corresponding character in second half is a vowel
20            vowelDifference -= vowelSet.count(s[i + halfLength]);
21        }
22      
23        // Return true if both halves have equal number of vowels
24        return vowelDifference == 0;
25    }
26};
27
1/**
2 * Determines if two halves of a string contain the same number of vowels
3 * @param s - The input string to check (must have even length)
4 * @returns true if both halves have equal vowel counts, false otherwise
5 */
6function halvesAreAlike(s: string): boolean {
7    // Create a set of vowels for O(1) lookup
8    const vowelSet: Set<string> = new Set(['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U']);
9  
10    // Initialize counter for vowel difference between halves
11    let vowelDifference: number = 0;
12  
13    // Calculate the midpoint of the string
14    const midpoint: number = s.length >> 1;
15  
16    // Iterate through the first half of the string
17    for (let i: number = 0; i < midpoint; i++) {
18        // Increment counter if character in first half is a vowel
19        if (vowelSet.has(s[i])) {
20            vowelDifference += 1;
21        }
22      
23        // Decrement counter if corresponding character in second half is a vowel
24        if (vowelSet.has(s[midpoint + i])) {
25            vowelDifference -= 1;
26        }
27    }
28  
29    // Return true if both halves have equal vowel counts (difference is 0)
30    return vowelDifference === 0;
31}
32

Time and Space Complexity

The time complexity is O(n), where n is the length of the string s. The algorithm iterates through the first half of the string (which is n/2 iterations), and for each iteration, it performs constant-time operations: checking if characters are in the vowels set and updating the counter. Since n/2 iterations simplifies to O(n), the overall time complexity is linear.

The space complexity is O(1) or O(C), where C is the number of vowel characters (which equals 10 in this case: 'a', 'e', 'i', 'o', 'u' and their uppercase versions). The algorithm uses a fixed-size set to store the vowels and a few integer variables (cnt and n). Since the vowels set has a constant size regardless of the input string length, the space complexity is constant.

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

Common Pitfalls

1. Incorrect String Length Calculation

A common mistake is assuming the string length is always even without validation, or using incorrect division for calculating the midpoint.

Pitfall Example:

# Wrong: Using floor division when length is odd (though problem guarantees even length)
n = len(s) // 2  # Could be problematic if not guaranteed even

# Wrong: Off-by-one error in indexing
for i in range(n + 1):  # This would go beyond the first half
    cnt += s[i] in vowels
    cnt -= s[i + n] in vowels  # Could cause IndexError

Solution: Always use the correct half-length calculation and ensure proper bounds:

half_length = len(s) // 2  # Correct for even-length strings
for i in range(half_length):  # Proper range
    # Process characters safely

2. Case Sensitivity Issues

Forgetting to include both uppercase and lowercase vowels in the vowel set.

Pitfall Example:

# Wrong: Only lowercase vowels
vowels = {'a', 'e', 'i', 'o', 'u'}  # Missing uppercase variants

# This would fail for input like "AbCdEfGh"

Solution: Include both cases in the vowel set:

vowels = {'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'}
# Or use: vowels = set('aeiouAEIOU')

3. Using String Methods Instead of Set Lookup

Using string containment check instead of set for vowel checking, leading to worse performance.

Pitfall Example:

# Inefficient: O(k) lookup where k is number of vowels
vowels = "aeiouAEIOU"
if s[i] in vowels:  # String search is O(10) for each check
    cnt += 1

Solution: Use a set for O(1) average-case lookup:

vowels = set('aeiouAEIOU')  # Set provides O(1) lookup
if s[i] in vowels:
    cnt += 1

4. Separate Counting Instead of Difference Tracking

Maintaining two separate counters and comparing them at the end, which uses more memory and is less elegant.

Pitfall Example:

# Less efficient approach
first_half_vowels = 0
second_half_vowels = 0
for i in range(half_length):
    if s[i] in vowels:
        first_half_vowels += 1
for i in range(half_length, len(s)):
    if s[i] in vowels:
        second_half_vowels += 1
return first_half_vowels == second_half_vowels

Solution: Use a single counter that tracks the difference:

cnt = 0
for i in range(half_length):
    cnt += s[i] in vowels
    cnt -= s[i + half_length] in vowels
return cnt == 0

5. Boolean to Integer Conversion Confusion

Not understanding that Python converts True to 1 and False to 0 in arithmetic operations.

Pitfall Example:

# Verbose and unnecessary
if s[i] in vowels:
    cnt += 1
else:
    cnt += 0  # Redundant

if s[i + n] in vowels:
    cnt -= 1
else:
    cnt -= 0  # Redundant

Solution: Leverage Python's implicit boolean-to-integer conversion:

cnt += s[i] in vowels        # Automatically adds 1 if True, 0 if False
cnt -= s[i + n] in vowels    # Automatically subtracts 1 if True, 0 if False
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