1704. Determine if String Halves Are Alike
Problem Description
In this problem, you are given a string s
with an even number of characters. Your task is to divide the string into two equal parts: a
which is the first half, and b
which is the second half of the string s
. After the split, you must determine whether the two strings are "alike." Two strings are considered alike if they contain the same number of vowels. Vowels are the characters 'a', 'e', 'i', 'o', and 'u' in both lowercase and uppercase forms. You need to return true
if the substrings a
and b
are alike (i.e., have the same number of vowels). Otherwise, return false
.
Intuition
To solve this problem, you need to count the number of vowels in both halves of the string and compare them. The most straightforward approach is to iterate through each half of the string separately, count the vowels in each half, and then compare the counts.
However, the provided solution takes a more efficient approach by calculating the difference in the number of vowels between both halves in a single pass. It uses a counter cnt
that is incremented when a vowel is encountered in the first half and decremented when a vowel is found in the second half. By doing this, we combine the counting and comparison steps.
To check if a character is a vowel, the solution creates a set vowels
containing all the vowels in both lowercase and uppercase. Using a set provides an efficient O(1) time complexity for checking if an element is present.
The variable n
represents half the length of the string. During the iteration, for each character in the first half (indexed by i
), cnt
is increased by 1 if it's a vowel. Correspondingly, for each character in the second half (indexed by i + n
), cnt
is decreased by 1 if it's a vowel.
If, after the single pass through the string, cnt
is 0
, this means the number of vowels in both halves are equal, and the two halves are alike (the function will return true
). If cnt
is not 0
, there is a different number of vowels in each half (the function will return false
).
Solution Approach
The provided solution uses a counting approach combined with a set to efficiently check for vowels in each half of the string. Here's a detailed walkthrough of the algorithm and the data structures used:
-
Initialization:
- A set
vowels
is created containing all vowel characters. This data structure is chosen because it allows for O(1) time complexity for vowel checks. - A variable
cnt
is initialized to0
. It represents the net difference in the number of vowels between the first half and the second half of the string. - The length of the first half of the string, denoted as
n
, is calculated usinglen(s) >> 1
, which is equivalent to dividing the length of the strings
by 2 using a bitwise right shift operator.
- A set
-
Iteration:
- We loop through
s
from the start to the midpoint(range(n))
. For each character in the first half:- We increment
cnt
if the character is found in thevowels
set (indicating that it's a vowel).
- We increment
- Simultaneously, for the corresponding character in the second half (indexed by
i + n
):- We decrement
cnt
if this character is a vowel.
- We decrement
- We loop through
During this single loop over the string, the cnt
variable effectively counts the number of vowels in the first half and subtracts the number of vowels in the second half. If the two halves contain an equal number of vowels, cnt
will be 0
at the end of the loop:
- Checking the result:
- The
cnt
variable is compared to0
. If they are equal, it means that there were an equal number of vowels in both halves, and we returntrue
. - If
cnt
is not0
, it indicates that the number of vowels in the two halves was different, and we returnfalse
.
- The
This method is efficient because it traverses the string only once and performs the counting and comparison at the same time, which gives it a time complexity of O(n), where n is the length of the string. Additionally, since the solution requires a fixed amount of extra space for the vowel set and the counter, its space complexity is O(1).
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's illustrate the solution approach using a small example. Suppose we are given the string s = "book"
.
-
Initialization:
- We create a set
vowels
= {'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'}. - We initialize
cnt
to0
. - Calculate half the length of the string
n = len(s) >> 1
which is2
.
- We create a set
-
Iteration:
- As we loop through the first half of the string
s
, we have 'b' and 'o'.- 'b' is not in
vowels
, socnt
remains0
. - 'o' is in
vowels
, so we incrementcnt
by1
. Nowcnt = 1
.
- 'b' is not in
- For the second half of the string, the corresponding indices will be
i + n = 2
andi + n = 3
, which gives us 'o' and 'k'.- 'o' is in
vowels
, so we decrementcnt
by1
. Nowcnt
returns to0
. - 'k' is not in
vowels
, socnt
remains0
.
- 'o' is in
- As we loop through the first half of the string
-
Checking the result:
- After the iteration,
cnt
is0
. - Since
cnt
is0
, it indicates that both halves of the string contain the same number of vowels. - Therefore, we return
true
. The string "book" has equal numbers of vowels in both halves, making them alike.
- After the iteration,
In this example, the single pass counting of vowels in both halves efficiently concludes that the string halves are alike without the need for separate counts.
Solution Implementation
1class Solution:
2 def halvesAreAlike(self, s: str) -> bool:
3 # Initialize a count variable for tracking vowel balance and
4 # calculate the midpoint of the string.
5 vowel_count_balance, string_length_half = 0, len(s) // 2
6
7 # Define a set containing all lowercase and uppercase vowels.
8 vowels = set('aeiouAEIOU')
9
10 # Iterate through the first half of the string.
11 for i in range(string_length_half):
12 # If the character in the first half is a vowel, increment the balance counter.
13 vowel_count_balance += s[i] in vowels
14
15 # If the corresponding character in the second half is a vowel, decrement the balance counter.
16 vowel_count_balance -= s[i + string_length_half] in vowels
17
18 # If the balance counter is zero after the loop, both halves have the same number of vowels.
19 return vowel_count_balance == 0
20
1class Solution {
2 // Create a set that holds all the vowel characters (both lower and upper case)
3 private static final Set<Character> VOWELS = Set.of('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U');
4
5 /**
6 * This method checks if a string has equal number of vowels in both halves.
7 *
8 * @param s The input string to be checked.
9 * @return Returns true if both halves have the same number of vowels, false otherwise.
10 */
11 public boolean halvesAreAlike(String s) {
12 // Initialize a variable to keep the cumulative difference of vowels count
13 int vowelCountDifference = 0;
14 // n represents half the length of the input string, using bitwise right shift for division by 2
15 int n = s.length() >> 1;
16
17 // Loop through each character of the first half and the corresponding character of the second half
18 for (int i = 0; i < n; ++i) {
19 // If the character at current position of first half is vowel, increment the difference count
20 vowelCountDifference += VOWELS.contains(s.charAt(i)) ? 1 : 0;
21 // If the character at the corresponding position in second half is vowel, decrement the difference count
22 vowelCountDifference -= VOWELS.contains(s.charAt(i + n)) ? 1 : 0;
23 }
24
25 // If vowelCountDifference is zero, it means both halves have the same number of vowels
26 return vowelCountDifference == 0;
27 }
28}
29
1#include <string>
2#include <unordered_set>
3using namespace std;
4
5class Solution {
6public:
7 bool halvesAreAlike(string s) {
8 // Define a set of vowels including both uppercase and lowercase.
9 unordered_set<char> vowels = {'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'};
10
11 // Initialize a counter to compare the number of vowels in each half.
12 int vowelBalance = 0;
13
14 // Calculate the midpoint of the string.
15 int midPoint = s.size() / 2;
16
17 // Loop to compare vowels in two halves of the string.
18 for (int i = 0; i < midPoint; ++i) {
19 // If the character in the first half is a vowel, increment the counter.
20 vowelBalance += vowels.count(s[i]);
21
22 // If the character in the second half is a vowel, decrement the counter.
23 vowelBalance -= vowels.count(s[i + midPoint]);
24 }
25
26 // The halves are alike if the vowelBalance is 0 (equal number of vowels).
27 return vowelBalance == 0;
28 }
29};
30
1// Function to check if two halves of a string contain the same number of vowels
2function halvesAreAlike(s: string): boolean {
3 // Define a set of vowels for quick lookup
4 const vowelsSet = new Set(['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U']);
5
6 // Calculate the half length of the string
7 const halfLength = s.length >> 1; // Equivalent to division by 2 using bitwise right shift
8
9 // Initialize a counter to compare vowel occurrences in both halves
10 let counter = 0;
11
12 // Loop through each character of the first half of the string
13 for (let i = 0; i < halfLength; i++) {
14 // If the character is a vowel, increment the counter
15 if (vowelsSet.has(s[i])) {
16 counter++;
17 }
18
19 // Simultaneously check the corresponding character in the second half
20 // If it is a vowel, decrement the counter
21 if (vowelsSet.has(s[halfLength + i])) {
22 counter--;
23 }
24 }
25
26 // If the counter is zero, both halves have the same number of vowels
27 return counter === 0;
28}
29
Time and Space Complexity
The given code snippet defines a function halvesAreAlike
that checks if two halves of a string contain the same number of vowels.
Time Complexity:
To analyze the time complexity, we need to consider the operations that depend on the size of the input string s
.
- The length of the string
s
is determined withlen(s)
, which isO(1)
. - The right shift operation
len(s) >> 1
is alsoO(1)
. - Creating the
vowels
set isO(1)
as it is initialized with a fixed number of characters. - The for loop runs
n
times, wheren
is half the length of the string. Inside the loop, we check the membership ofs[i]
ands[i + n]
in thevowels
set, and then update thecnt
variable accordingly. The set membership checking isO(1)
on average. Thus, the loop has a complexity ofO(n)
.
Given that n
is len(s) / 2
, the time complexity of the entire function is dominated by the for loop, which gives us O(n/2)
. However, in big O notation, we drop constant factors, so the time complexity can be expressed as O(n)
where n
is the length of the string s
.
Space Complexity:
- The space complexity is determined by the additional space used by the algorithm independent of the input size.
- The
vowels
set requiresO(1)
space because it contains a fixed number of vowels, which does not grow with the input. - The
cnt
variable also takesO(1)
space. - The function does not use any data structures that grow with the input size.
Therefore, the space complexity of the function is O(1)
.
In conclusion, the time complexity is O(n)
and the space complexity is O(1)
.
Learn more about how to find time and space complexity quickly using problem constraints.
What does the following code do?
1def f(arr1, arr2):
2 i, j = 0, 0
3 new_arr = []
4 while i < len(arr1) and j < len(arr2):
5 if arr1[i] < arr2[j]:
6 new_arr.append(arr1[i])
7 i += 1
8 else:
9 new_arr.append(arr2[j])
10 j += 1
11 new_arr.extend(arr1[i:])
12 new_arr.extend(arr2[j:])
13 return new_arr
14
1public static List<Integer> f(int[] arr1, int[] arr2) {
2 int i = 0, j = 0;
3 List<Integer> newArr = new ArrayList<>();
4
5 while (i < arr1.length && j < arr2.length) {
6 if (arr1[i] < arr2[j]) {
7 newArr.add(arr1[i]);
8 i++;
9 } else {
10 newArr.add(arr2[j]);
11 j++;
12 }
13 }
14
15 while (i < arr1.length) {
16 newArr.add(arr1[i]);
17 i++;
18 }
19
20 while (j < arr2.length) {
21 newArr.add(arr2[j]);
22 j++;
23 }
24
25 return newArr;
26}
27
1function f(arr1, arr2) {
2 let i = 0, j = 0;
3 let newArr = [];
4
5 while (i < arr1.length && j < arr2.length) {
6 if (arr1[i] < arr2[j]) {
7 newArr.push(arr1[i]);
8 i++;
9 } else {
10 newArr.push(arr2[j]);
11 j++;
12 }
13 }
14
15 while (i < arr1.length) {
16 newArr.push(arr1[i]);
17 i++;
18 }
19
20 while (j < arr2.length) {
21 newArr.push(arr2[j]);
22 j++;
23 }
24
25 return newArr;
26}
27
Recommended Readings
LeetCode 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 we
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 algomonster s3 us east 2 amazonaws com recursion jpg You first
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!