Determine if String Halves Are Alike
EasyPrompt
You are given a string s of even length. Split this string into two halves of equal lengths, and let a be the first half and b be the second half.
Two strings are alike if they have the same number of vowels ('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'). Notice that s contains uppercase and lowercase letters.
Return true if a and b are alike. Otherwise, return false.
Example 1:
Input: s = "book"
Output: true
Explanation: a = "bo" and b = "ok". a has 1 vowel and b has 1 vowel. Therefore, they are alike.Example 2:
Input: s = "textbook"
Output: false
Explanation: a = "text" and b = "book". a has 1 vowel whereas b has 2. Therefore, they are not alike.
Notice that the vowel o is counted twice.
Constraints:
2 <= s.length <= 1000s.lengthis even.sconsists of uppercase and lowercase letters.
Approaches
3 approaches with complexity analysis and trade-offs.
This approach involves splitting the input string into two separate substrings representing the first and second halves. Then, it counts the vowels in each substring independently and compares the counts.
Algorithm
- Calculate the middle index:
mid = s.length() / 2. - Create the first half substring
afrom index 0 tomid. - Create the second half substring
bfrom indexmidto the end. - Define a helper function
countVowels(String str):- Initialize a vowel counter to 0.
- Define a string containing all vowels:
"aeiouAEIOU". - Iterate through each character of the input string
str. - If the character is found in the vowels string, increment the counter.
- Return the final count.
- Call
countVowelsfor bothaandb. - Return
trueif the counts are equal,falseotherwise.
Walkthrough
The core idea is to first divide the problem into two smaller, identical subproblems: counting vowels in a string.
First, we calculate the middle index of the string s.
Using the substring method, we create two new strings: a for the first half (from index 0 to mid-1) and b for the second half (from index mid to the end).
We then iterate through each of these new substrings. For each character, we check if it's a vowel. A simple way to check for a vowel is to see if the character exists in a predefined string of all vowels ("aeiouAEIOU").
We maintain two separate counters, one for each half. After iterating through both substrings, we compare the final counts. If they are equal, the halves are alike; otherwise, they are not.
class Solution { public boolean halvesAreAlike(String s) { int mid = s.length() / 2; String a = s.substring(0, mid); String b = s.substring(mid); return countVowels(a) == countVowels(b); } private int countVowels(String str) { int count = 0; String vowels = "aeiouAEIOU"; for (char c : str.toCharArray()) { if (vowels.indexOf(c) != -1) { count++; } } return count; }}Complexity
Time
O(N), where N is the length of the string `s`. Creating substrings takes O(N) time. The `countVowels` function also iterates through N/2 characters for each half, resulting in a total of O(N) operations. The `indexOf` check on a small, constant-size string is effectively O(1).
Space
O(N), where N is the length of the string `s`. This is because creating two substrings of length N/2 requires allocating new memory proportional to the length of the original string.
Trade-offs
Pros
The logic is very clear and easy to follow.
It modularizes the problem well by using a helper function for counting vowels.
Cons
It's not space-efficient. Creating new substrings consumes O(N) extra memory, which is unnecessary.
This approach improves on the previous one by avoiding the creation of new substrings. It iterates through the original string in two separate passes—one for each half—to count the vowels, thus using only constant extra space.
Algorithm
- Create a
Setof all vowel characters for O(1) lookup. - Calculate the middle index:
mid = s.length() / 2. - Initialize two counters,
firstHalfCountandsecondHalfCount, to 0. - Iterate from
i = 0tomid - 1:- Get the character
s.charAt(i). - If the character is in the vowel set, increment
firstHalfCount.
- Get the character
- Iterate from
i = midtos.length() - 1:- Get the character
s.charAt(i). - If the character is in the vowel set, increment
secondHalfCount.
- Get the character
- Return
trueiffirstHalfCountequalssecondHalfCount,falseotherwise.
Walkthrough
Instead of allocating memory for new substrings, we can work directly with the input string s. This eliminates the O(N) space overhead of the previous method.
We first establish a quick way to check for vowels. A HashSet containing all vowel characters ('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U') is ideal, as it provides an average time complexity of O(1) for lookups.
The algorithm then proceeds in two loops. The first loop iterates from the beginning of the string to the midpoint, counting vowels in the first half. The second loop iterates from the midpoint to the end of the string, counting vowels in the second half.
Finally, the two counts are compared to determine if the halves are alike.
import java.util.Arrays;import java.util.HashSet;import java.util.Set; class Solution { public boolean halvesAreAlike(String s) { Set<Character> vowels = new HashSet<>(Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U')); int mid = s.length() / 2; int firstHalfCount = 0; int secondHalfCount = 0; // First pass: count vowels in the first half for (int i = 0; i < mid; i++) { if (vowels.contains(s.charAt(i))) { firstHalfCount++; } } // Second pass: count vowels in the second half for (int i = mid; i < s.length(); i++) { if (vowels.contains(s.charAt(i))) { secondHalfCount++; } } return firstHalfCount == secondHalfCount; }}Complexity
Time
O(N), where N is the length of the string. We iterate through the entire string once (split across two loops). The `HashSet` creation is a constant time operation, and lookups are O(1) on average.
Space
O(1). The extra space used is for the `HashSet` of vowels, which has a constant size (10 characters), regardless of the input string's length.
Trade-offs
Pros
Space-efficient, as it avoids creating new strings.
Maintains good readability.
Cons
Requires two separate loops to iterate over the string, which is slightly less elegant than a single-pass solution.
This is the most efficient approach. It uses a single loop to iterate through the first half of the string. In each iteration, it checks one character from the first half and its corresponding character from the second half, updating a single counter to track the difference in vowel counts.
Algorithm
- Create a
Setof all vowel characters for O(1) lookup. - Calculate the middle index:
mid = s.length() / 2. - Initialize a counter
vowelDifferenceto 0. - Iterate with an index
ifrom 0 tomid - 1:- Check if the character at
s.charAt(i)(first half) is a vowel. If yes, incrementvowelDifference. - Check if the character at
s.charAt(i + mid)(second half) is a vowel. If yes, decrementvowelDifference.
- Check if the character at
- After the loop, return
trueifvowelDifferenceis 0,falseotherwise.
Walkthrough
This method optimizes the process by combining the counting for both halves into a single loop. It iterates from the beginning of the string up to the midpoint.
A single counter, let's call it vowelDifference, is initialized to zero.
Inside the loop, for each index i in the first half, we examine s.charAt(i) and s.charAt(i + mid), where mid is s.length() / 2.
If the character from the first half (s.charAt(i)) is a vowel, we increment vowelDifference.
If the character from the second half (s.charAt(i + mid)) is a vowel, we decrement vowelDifference.
After the loop completes, if vowelDifference is zero, it means the number of vowels added from the first half is perfectly balanced by the number of vowels subtracted from the second half. Therefore, the vowel counts are equal.
import java.util.Arrays;import java.util.HashSet;import java.util.Set; class Solution { public boolean halvesAreAlike(String s) { Set<Character> vowels = new HashSet<>(Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U')); int mid = s.length() / 2; int vowelDifference = 0; for (int i = 0; i < mid; i++) { if (vowels.contains(s.charAt(i))) { vowelDifference++; } if (vowels.contains(s.charAt(i + mid))) { vowelDifference--; } } return vowelDifference == 0; }}Complexity
Time
O(N), where N is the length of the string. The loop runs N/2 times, and in each iteration, we perform two character lookups and two hash set lookups. This results in a linear time complexity proportional to N.
Space
O(1). The extra space is constant, used only for the `HashSet` of vowels.
Trade-offs
Pros
Most efficient in terms of both time and space.
Processes the string in a single pass, which can be slightly faster in practice due to better cache utilization and less loop overhead.
Elegant and concise code.
Cons
The logic of using a single difference counter might be slightly less intuitive for beginners compared to using two separate counters.
Solutions
Solution
class Solution {private static final Set<Character> VOWELS = Set.of('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U');public boolean halvesAreAlike(String s) { int cnt = 0, n = s.length() >> 1; for (int i = 0; i < n; ++i) { cnt += VOWELS.contains(s.charAt(i)) ? 1 : 0; cnt -= VOWELS.contains(s.charAt(i + n)) ? 1 : 0; } return cnt == 0; }}/** * @param {string} s * @return {boolean} */ var halvesAreAlike = function ( s ) { const str = ' aeiouAEIOU ' ; let cnt = 0 ; for ( let i = 0 ; i < s . length / 2 ; i ++ ) { if ( str . indexOf ( s [ i ]) > - 1 ) cnt ++ ; if ( str . indexOf ( s [ s . length - 1 - i ]) > - 1 ) cnt -- ; } return cnt === 0 ; };class Solution {public: bool halvesAreAlike(string s) { unordered_set<char> vowels = {'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'}; int cnt = 0, n = s.size() / 2; for (int i = 0; i < n; ++i) { cnt += vowels.count(s[i]); cnt -= vowels.count(s[i + n]); } return cnt == 0; }};class Solution: def halvesAreAlike(self, s: str) -> bool: cnt, n = 0, len(s) >> 1 vowels = set('aeiouAEIOU') for i in range(n): cnt += s[i] in vowels cnt -= s[i + n] in vowels return cnt == 0Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.