Talentd
FleetCode
Make and follow sheets.Sign in

Count Beautiful Substrings II solution and explanation

HardHash TableMathNumber TheoryPrefix Sum6 min read27.5% acceptanceUpdated 9 Apr 2026
Count Beautiful Substrings II | Detailed Approach | Intuition | Leetcode - 2949 | Weekly Contest 373codestorywithMIK · 52:40 · 7K viewsWatch on YouTube
4 more videos on this problem
  1. Count Beautiful Substrings II | Detailed Approach | Intuition | Leetcode - 2949 | Weekly Contest 373codestorywithMIK · 52:40 · 7K views
  2. 2949. Count Beautiful Substrings II | Weekly Leetcode 373codingMohan · 38:49 · 2K views
  3. 2949. Count Beautiful Substrings II (Leetcode Hard)Programming Live with Larry · 37:47 · 603 views
  4. 【每日一题】LeetCode 2949. Count Beautiful Substrings IIHuifeng Guan · 22:32 · 483 views
  5. Count Beautiful Substrings II - LeetCode 2949 - PythonAlpha-Code · 39:54 · 256 views

The problem

You are given a string s and a positive integer k.

Let vowels and consonants be the number of vowels and consonants in a string.

A string is beautiful if:

  • vowels == consonants.
  • (vowels * consonants) % k == 0, in other terms the multiplication of vowels and consonants is divisible by k.

Return the number of non-empty beautiful substrings in the given string s.

A substring is a contiguous sequence of characters in a string.

Vowel letters in English are 'a', 'e', 'i', 'o', and 'u'.

Consonant letters in English are every letter except vowels.

Example 1:

Input: s = "baeyh", k = 2
Output: 2
Explanation: There are 2 beautiful substrings in the given string.
- Substring "baeyh", vowels = 2 (["a",e"]), consonants = 2 (["y","h"]).
You can see that string "aeyh" is beautiful as vowels == consonants and vowels * consonants % k == 0.
- Substring "baeyh", vowels = 2 (["a",e"]), consonants = 2 (["b","y"]).
You can see that string "baey" is beautiful as vowels == consonants and vowels * consonants % k == 0.
It can be shown that there are only 2 beautiful substrings in the given string.

Example 2:

Input: s = "abba", k = 1
Output: 3
Explanation: There are 3 beautiful substrings in the given string.
- Substring "abba", vowels = 1 (["a"]), consonants = 1 (["b"]).
- Substring "abba", vowels = 1 (["a"]), consonants = 1 (["b"]).
- Substring "abba", vowels = 2 (["a","a"]), consonants = 2 (["b","b"]).
It can be shown that there are only 3 beautiful substrings in the given string.

Example 3:

Input: s = "bcdf", k = 1
Output: 0
Explanation: There are no beautiful substrings in the given string.

Constraints:

  • 1 <= s.length <= 5 * 104
  • 1 <= k <= 1000
  • s consists of only English lowercase letters.

Approach overview

Problem Overview: You are given a string and an integer k. A substring is considered beautiful when the number of vowels equals the number of consonants and the product of these counts is divisible by k. The task is to count how many such substrings exist.

Approach 1: Brute Force with Prefix Counts (O(n2) time, O(n) space)

The straightforward solution checks every possible substring. Precompute prefix counts of vowels so you can quickly calculate the number of vowels and consonants in any range. For each pair (i, j), compute vowels and consonants, verify they are equal, then check whether (vowels * consonants) % k == 0. This approach uses prefix sum to avoid recomputing counts, but still requires iterating over all O(n2) substrings. It works for small inputs and helps validate logic before implementing the optimized method.

Approach 2: Prefix Difference + Hash Table with Number Theory (O(n) time, O(n) space)

The key observation: if vowels equal consonants, then v = c. Let v be the number of vowels. The condition becomes v * v % k == 0. Using number theory, compute the smallest integer t such that t * t is divisible by k. That means v must be a multiple of t. Since the substring length is 2v, the length must be divisible by 2t.

While scanning the string, maintain a prefix difference diff = vowels - consonants. Two indices i and j form a valid substring when their diff values match. To enforce the length divisibility constraint, also track the index modulo 2t. Store counts in a hash table keyed by (diff, index % (2t)). When the same key appears again, all previous occurrences represent valid starting points for beautiful substrings ending at the current index. This reduces the problem to constant-time hash lookups per character.

Recommended for interviews: The brute force approach shows you understand the substring conditions and how prefix counts simplify range queries. Interviewers typically expect the optimized prefix-difference solution combined with a hash map and number-theory insight. It reduces the complexity from O(n2) to O(n) and demonstrates strong pattern recognition with prefix states + hashing.

Approach 1: Brute Force Approach

In this approach, we consider all possible substrings of the given string. For each substring, we count the number of vowels and consonants to check if the substring satisfies the conditions to be beautiful.

Iterate over all possible substrings, counting vowels and consonants, and check both conditions for each substring. This approach is straightforward but not very efficient for large strings.

def is_vowel(ch):
    return ch in 'aeiou'


def count_beautiful_substrings(s, k):
    n = len(s)
    count = 0
    for i in range(n):
        vowels = 0
        consonants = 0
        for j in range(i, n):
            if is_vowel(s[j]):
                vowels += 1
            else:
                consonants += 1
            if vowels == consonants and (vowels * consonants) % k == 0:
                count += 1
    return count


# Example usage
s = "baeyh"
k = 2
print(count_beautiful_substrings(s, k))
public class Main {
    public static boolean isVowel(char ch) {
        return "aeiou".indexOf(ch) != -1;
    }

    public static int countBeautifulSubstrings(String s, int k) {
        int n = s.length();
        int count = 0;
        for (int i = 0; i < n; i++) {
            int vowels = 0, consonants = 0;
            for (int j = i; j < n; j++) {
                if (isVowel(s.charAt(j))) {
                    vowels++;
                } else {
                    consonants++;
                }
                if (vowels == consonants && (vowels * consonants) % k == 0) {
                    count++;
                }
            }
        }
        return count;
    }

    public static void main(String[] args) {
        String s = "baeyh";
        int k = 2;
        System.out.println(countBeautifulSubstrings(s, k));
    }
}
#include <iostream>
#include <string>
using namespace std;

bool isVowel(char ch) {
    return ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u';
}

int countBeautifulSubstrings(const string& s, int k) {
    int n = s.size();
    int count = 0;
    for (int i = 0; i < n; i++) {
        int vowels = 0, consonants = 0;
        for (int j = i; j < n; j++) {
            if (isVowel(s[j])) {
                vowels++;
            } else {
                consonants++;
            }
            if (vowels == consonants && (vowels * consonants) % k == 0) {
                count++;
            }
        }
    }
    return count;
}

int main() {
    string s = "baeyh";
    int k = 2;
    cout << countBeautifulSubstrings(s, k) << endl;
    return 0;
}
function isVowel(ch) {
    return 'aeiou'.includes(ch);
}

function countBeautifulSubstrings(s, k) {
    let count = 0;
    let n = s.length;
    for (let i = 0; i < n; i++) {
        let vowels = 0;
        let consonants = 0;
        for (let j = i; j < n; j++) {
            if (isVowel(s[j])) {
                vowels++;
            } else {
                consonants++;
            }
            if (vowels === consonants && (vowels * consonants) % k === 0) {
                count++;
            }
        }
    }
    return count;
}

console.log(countBeautifulSubstrings('baeyh', 2));
#include <stdio.h>
#include <string.h>

int isVowel(char ch) {
    return ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u';
}

int countBeautifulSubstrings(char* s, int k) {
    int n = strlen(s);
    int count = 0;
    for (int i = 0; i < n; i++) {
        int vowels = 0, consonants = 0;
        for (int j = i; j < n; j++) {
            if (isVowel(s[j])) {
                vowels++;
            } else {
                consonants++;
            }
            if (vowels == consonants && (vowels * consonants) % k == 0) {
                count++;
            }
        }
    }
    return count;
}

int main() {
    char s[] = "baeyh";
    int k = 2;
    printf("%d\n", countBeautifulSubstrings(s, k));
    return 0;
}
using System;

public class BeautifulSubstrings {
    public static bool IsVowel(char ch) {
        return "aeiou".IndexOf(ch) != -1;
    }

    public static int CountBeautifulSubstrings(string s, int k) {
        int n = s.Length;
        int count = 0;
        for (int i = 0; i < n; i++) {
            int vowels = 0, consonants = 0;
            for (int j = i; j < n; j++) {
                if (IsVowel(s[j])) {
                    vowels++;
                } else {
                    consonants++;
                }
                if (vowels == consonants && (vowels * consonants) % k == 0) {
                    count++;
                }
            }
        }
        return count;
    }

    public static void Main() {
        string s = "baeyh";
        int k = 2;
        Console.WriteLine(CountBeautifulSubstrings(s, k));
    }
}

The Python solution uses similar nested loops to enumerate substrings and check their properties. Libraries are not necessary for this brute force technique.

Complexity

Time Complexity: O(n^3).
Space Complexity: O(1).

Try this approach in the editor

Approach 2: Optimized Sliding Window or Prefix Array Approach

We can optimize the brute force approach by using a sliding window or prefix array technique. The sliding window keeps track of vowel and consonant counts while expanding or contracting the window for each character.

This method reduces redundant recalculations by leveraging previously computed results, improving efficiency by reducing time complexity.

def is_vowel(ch):
    return ch in 'aeiou'


def count_beautiful_substrings(s, k):
    n = len(s)
    count = 0
    prefix_vowels = [0] * (n + 1)
    prefix_consonants = [0] * (n + 1)
    for i in range(n):
        prefix_vowels[i + 1] = prefix_vowels[i] + (1 if is_vowel(s[i]) else 0)
        prefix_consonants[i + 1] = prefix_consonants[i] + (1 if not is_vowel(s[i]) else 0)

    for length in range(1, n + 1):
        for i in range(n - length + 1):
            j = i + length
            curr_vowels = prefix_vowels[j] - prefix_vowels[i]
            curr_consonants = prefix_consonants[j] - prefix_consonants[i]
            if curr_vowels == curr_consonants and (curr_vowels * curr_consonants) % k == 0:
                count += 1
    return count


# Example usage
s = "baeyh"
k = 2
print(count_beautiful_substrings(s, k))
public class Main {
    public static boolean isVowel(char ch) {
        return "aeiou".indexOf(ch) != -1;
    }

    public static int countBeautifulSubstrings(String s, int k) {
        int n = s.length();
        int count = 0;
        int[] prefixVowels = new int[n + 1];
        int[] prefixConsonants = new int[n + 1];
        for (int i = 0; i < n; i++) {
            prefixVowels[i + 1] = prefixVowels[i] + (isVowel(s.charAt(i)) ? 1 : 0);
            prefixConsonants[i + 1] = prefixConsonants[i] + (!isVowel(s.charAt(i)) ? 1 : 0);
        }
        for (int len = 1; len <= n; len++) {
            for (int i = 0; i + len <= n; i++) {
                int j = i + len;
                int currVowels = prefixVowels[j] - prefixVowels[i];
                int currConsonants = prefixConsonants[j] - prefixConsonants[i];
                if (currVowels == currConsonants && (currVowels * currConsonants) % k == 0) {
                    count++;
                }
            }
        }
        return count;
    }

    public static void main(String[] args) {
        String s = "baeyh";
        int k = 2;
        System.out.println(countBeautifulSubstrings(s, k));
    }
}
#include <iostream>
#include <vector>
#include <string>
using namespace std;

bool isVowel(char ch) {
    return ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u';
}

int countBeautifulSubstrings(const string& s, int k) {
    int n = s.size();
    int count = 0;
    vector<int> prefixVowels(n + 1, 0);
    vector<int> prefixConsonants(n + 1, 0);
    for (int i = 0; i < n; i++) {
        prefixVowels[i + 1] = prefixVowels[i] + (isVowel(s[i]) ? 1 : 0);
        prefixConsonants[i + 1] = prefixConsonants[i] + (!isVowel(s[i]) ? 1 : 0);
    }
    for (int len = 1; len <= n; len++) {
        for (int i = 0; i + len <= n; i++) {
            int j = i + len;
            int currVowels = prefixVowels[j] - prefixVowels[i];
            int currConsonants = prefixConsonants[j] - prefixConsonants[i];
            if (currVowels == currConsonants && (currVowels * currConsonants) % k == 0) {
                count++;
            }
        }
    }
    return count;
}

int main() {
    string s = "baeyh";
    int k = 2;
    cout << countBeautifulSubstrings(s, k) << endl;
    return 0;
}
function isVowel(ch) {
    return 'aeiou'.includes(ch);
}

function countBeautifulSubstrings(s, k) {
    let n = s.length;
    let count = 0;
    let prefixVowels = new Array(n + 1).fill(0);
    let prefixConsonants = new Array(n + 1).fill(0);

    for (let i = 0; i < n; i++) {
        prefixVowels[i + 1] = prefixVowels[i] + (isVowel(s[i]) ? 1 : 0);
        prefixConsonants[i + 1] = prefixConsonants[i] + (!isVowel(s[i]) ? 1 : 0);
    }

    for (let len = 1; len <= n; len++) {
        for (let i = 0; i + len <= n; i++) {
            let j = i + len;
            let currVowels = prefixVowels[j] - prefixVowels[i];
            let currConsonants = prefixConsonants[j] - prefixConsonants[i];

            if (currVowels === currConsonants && (currVowels * currConsonants) % k === 0) {
                count++;
            }
        }
    }

    return count;
}

console.log(countBeautifulSubstrings('baeyh', 2));
#include <stdio.h>
#include <string.h>

int isVowel(char ch) {
    return ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u';
}

int countBeautifulSubstrings(char* s, int k) {
    int n = strlen(s);
    int count = 0;
    int vowels = 0, consonants = 0;
    int prefixVowels[n + 1];
    int prefixConsonants[n + 1];
    prefixVowels[0] = 0;
    prefixConsonants[0] = 0;
    for (int i = 0; i < n; i++) {
        if (isVowel(s[i])) {
            vowels++;
        } else {
            consonants++;
        }
        prefixVowels[i + 1] = vowels;
        prefixConsonants[i + 1] = consonants;
    }
    for (int len = 1; len <= n; len++) {
        for (int i = 0; i + len <= n; i++) {
            int j = i + len;
            int currVowels = prefixVowels[j] - prefixVowels[i];
            int currConsonants = prefixConsonants[j] - prefixConsonants[i];
            if (currVowels == currConsonants && (currVowels * currConsonants) % k == 0) {
                count++;
            }
        }
    }
    return count;
}

int main() {
    char s[] = "baeyh";
    int k = 2;
    printf("%d\n", countBeautifulSubstrings(s, k));
    return 0;
}
using System;

public class BeautifulSubstrings {
    public static bool IsVowel(char ch) {
        return "aeiou".IndexOf(ch) != -1;
    }

    public static int CountBeautifulSubstrings(string s, int k) {
        int n = s.Length;
        int count = 0;
        int[] prefixVowels = new int[n + 1];
        int[] prefixConsonants = new int[n + 1];
        for (int i = 0; i < n; i++) {
            prefixVowels[i + 1] = prefixVowels[i] + (IsVowel(s[i]) ? 1 : 0);
            prefixConsonants[i + 1] = prefixConsonants[i] + (!IsVowel(s[i]) ? 1 : 0);
        }

        for (int len = 1; len <= n; len++) {
            for (int i = 0; i + len <= n; i++) {
                int j = i + len;
                int currVowels = prefixVowels[j] - prefixVowels[i];
                int currConsonants = prefixConsonants[j] - prefixConsonants[i];
                if (currVowels == currConsonants && (currVowels * currConsonants) % k == 0) {
                    count++;
                }
            }
        }
        return count;
    }

    public static void Main() {
        string s = "baeyh";
        int k = 2;
        Console.WriteLine(CountBeautifulSubstrings(s, k));
    }
}

Using Python's list operations, this solution calculates cumulative counts of vowels and consonants, allowing for rapid verification of substring conditions.

Complexity

Time Complexity: O(n^2).
Space Complexity: O(n).

Try this approach in the editor

Approach 3: Prefix Sum + Hash Table

function beautifulSubstrings(s: string, k: number): number {
    const l = pSqrt(k * 4);
    const n = s.length;
    let sum = n;
    let ans = 0;
    const counter = new Map();
    counter.set(((l - 1) << 17) | sum, 1);
    for (let i = 0; i < n; i++) {
        const char = s[i];
        const bit = (AEIOU_MASK >> (char.charCodeAt(0) - 'a'.charCodeAt(0))) & 1;
        sum += bit * 2 - 1; // 1 -> 1    0 -> -1
        const key = ((i % l) << 17) | sum;
        ans += counter.get(key) || 0; // ans += cnt[(i%k,sum)]++
        counter.set(key, (counter.get(key) ?? 0) + 1);
    }
    return ans;
}
const AEIOU_MASK = 1065233;

function pSqrt(n: number) {
    let res = 1;
    for (let i = 2; i * i <= n; i++) {
        let i2 = i * i;
        while (n % i2 == 0) {
            res *= i;
            n /= i2;
        }
        if (n % i == 0) {
            res *= i;
            n /= i;
        }
    }
    if (n > 1) {
        res *= n;
    }
    return res;
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Brute Force Approach

Time Complexity: O(n^3).
Space Complexity: O(1).

Optimized Sliding Window or Prefix Array Approach

Time Complexity: O(n^2).
Space Complexity: O(n).

Prefix Sum + Hash Table–

Complexity analysis

ApproachTimeSpaceWhen to Use
Brute Force with Prefix CountsO(n²)O(n)Small inputs or when validating substring conditions during learning
Prefix Difference + Hash Table + Number TheoryO(n)O(n)Optimal approach for large strings and typical interview expectations

Frequently asked questions

Is Count Beautiful Substrings II easy or hard?

Count Beautiful Substrings II is classified as a Hard problem. The difficulty comes from combining multiple concepts: prefix sums, hash table state compression, and number theory to derive the required substring length constraint.

Count Beautiful Substrings II Python/Java solution

Most implementations use the same algorithm across languages: compute the required period using number theory, maintain a running prefix difference, and store counts in a hash map. The logic translates directly to Python, Java, C++, or other languages with dictionary support.

How to solve Count Beautiful Substrings II in O(n)?

Compute the smallest integer t such that t^2 is divisible by k. While iterating through the string, maintain the prefix difference between vowels and consonants and store counts in a hash map keyed by (diff, index mod 2t). When the same key appears again, it forms valid beautiful substrings.

What is the best approach for Count Beautiful Substrings II?

The optimal approach uses prefix difference with a hash table combined with a number theory observation. Track the difference between vowels and consonants using prefix sums and group indices by (diff, index mod period). This allows counting valid substrings in O(n) time and O(n) space.

Is Count Beautiful Substrings II asked at Google/Amazon/Meta?

Problems involving prefix sums, hash maps, and substring counting frequently appear in interviews at companies like Google, Amazon, and Meta. Variations of this pattern are commonly used to test string processing and prefix-state hashing techniques.

What data structure is used in Count Beautiful Substrings II?

The optimized solution relies on a hash table (hash map) to store counts of prefix states. It also uses prefix sums to track vowel and consonant differences and applies number theory to enforce the divisibility constraint efficiently.

What is the time complexity of Count Beautiful Substrings II?

The brute force method runs in O(n^2) time because it checks every substring. The optimized approach runs in O(n) time by scanning the string once and using hash table lookups to count matching prefix states.

Comments

Now write it yourselfClose this page and solve Count Beautiful Substrings II in the editor, without looking.
Practise Count Beautiful Substrings II