Problem

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 ifa andb arealike. Otherwise, return false.

Examples

Example 1

1
2
3
Input: s = "book"
Output: true
Explanation: a = "b _o_ " and b = "_o_ k". a has 1 vowel and b has 1 vowel. Therefore, they are alike.

Example 2

1
2
3
4
Input: s = "textbook"
Output: false
Explanation: a = "t _e_ xt" and b = "b _oo_ k". 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 <= 1000
  • s.length is even.
  • s consists of uppercase and lowercase letters.

Solution

Method 1 – Vowel Counting

Intuition

If the two halves of the string have the same number of vowels, they are alike. We can count vowels in each half and compare.

Approach

  1. Define a set of vowels (both lowercase and uppercase).
  2. Split the string into two halves.
  3. Count the number of vowels in each half.
  4. Return true if the counts are equal, else false.

Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Solution {
public:
    bool halvesAreAlike(string s) {
        string v = "aeiouAEIOU";
        int n = s.size(), cnt1 = 0, cnt2 = 0;
        for (int i = 0; i < n / 2; ++i) if (v.find(s[i]) != string::npos) ++cnt1;
        for (int i = n / 2; i < n; ++i) if (v.find(s[i]) != string::npos) ++cnt2;
        return cnt1 == cnt2;
    }
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
func halvesAreAlike(s string) bool {
    vowels := "aeiouAEIOU"
    n := len(s)
    cnt1, cnt2 := 0, 0
    for i := 0; i < n/2; i++ {
        if strings.ContainsRune(vowels, rune(s[i])) {
            cnt1++
        }
    }
    for i := n / 2; i < n; i++ {
        if strings.ContainsRune(vowels, rune(s[i])) {
            cnt2++
        }
    }
    return cnt1 == cnt2
}
1
2
3
4
5
6
7
8
9
class Solution {
    public boolean halvesAreAlike(String s) {
        String v = "aeiouAEIOU";
        int n = s.length(), cnt1 = 0, cnt2 = 0;
        for (int i = 0; i < n / 2; ++i) if (v.indexOf(s.charAt(i)) != -1) ++cnt1;
        for (int i = n / 2; i < n; ++i) if (v.indexOf(s.charAt(i)) != -1) ++cnt2;
        return cnt1 == cnt2;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Solution {
    fun halvesAreAlike(s: String): Boolean {
        val v = "aeiouAEIOU"
        val n = s.length
        var cnt1 = 0
        var cnt2 = 0
        for (i in 0 until n / 2) if (v.contains(s[i])) cnt1++
        for (i in n / 2 until n) if (v.contains(s[i])) cnt2++
        return cnt1 == cnt2
    }
}
1
2
3
4
5
6
7
class Solution:
    def halvesAreAlike(self, s: str) -> bool:
        v = set('aeiouAEIOU')
        n = len(s)
        cnt1 = sum(1 for c in s[:n//2] if c in v)
        cnt2 = sum(1 for c in s[n//2:] if c in v)
        return cnt1 == cnt2
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
impl Solution {
    pub fn halves_are_alike(s: String) -> bool {
        let v = "aeiouAEIOU".chars().collect::<std::collections::HashSet<_>>();
        let n = s.len();
        let s = s.chars().collect::<Vec<_>>();
        let cnt1 = s[..n/2].iter().filter(|c| v.contains(c)).count();
        let cnt2 = s[n/2..].iter().filter(|c| v.contains(c)).count();
        cnt1 == cnt2
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Solution {
    halvesAreAlike(s: string): boolean {
        const v = new Set('aeiouAEIOU');
        const n = s.length;
        let cnt1 = 0, cnt2 = 0;
        for (let i = 0; i < n / 2; ++i) if (v.has(s[i])) ++cnt1;
        for (let i = n / 2; i < n; ++i) if (v.has(s[i])) ++cnt2;
        return cnt1 === cnt2;
    }
}

Complexity

  • ⏰ Time complexity: O(n), where n is the length of s. We scan the string once.
  • 🧺 Space complexity: O(1), as we use only a constant amount of extra space.