1704. Determine if String Halves Are Alike
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 firstn/2
characters - The second half
b
consists of the lastn/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 wheren = 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)
- The character at position
- 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 returnstrue
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
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 hasy
vowels - We want to check if
x == y
, which is equivalent to checking ifx - 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 characters[i]
- The corresponding character in the second half is at index
i + n
(wheren
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:
-
Initialize variables:
cnt = 0
: A counter to track the difference in vowel counts between the two halvesn = 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
-
Single-pass iteration:
for i in range(n): cnt += s[i] in vowels cnt -= s[i + n] in vowels
- Loop through indices from
0
ton-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
returnsTrue
(which equals 1), socnt
increases by 1 - If no, it returns
False
(which equals 0), socnt
remains unchanged
- If yes,
- 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)
- If yes, subtract 1 from
- Check if
- Loop through indices from
-
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 andFalse
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 EvaluatorExample 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
i | s[i] | s[i+n] | s[i] in vowels | s[i+n] in vowels | cnt calculation | cnt value |
---|---|---|---|---|---|---|
0 | 't' | 'b' | False (0) | False (0) | 0 + 0 - 0 | 0 |
1 | 'e' | 'o' | True (1) | True (1) | 0 + 1 - 1 | 0 |
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
, returnFalse
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
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!