2947. Count Beautiful Substrings I
Problem Description
In this LeetCode problem, we are given a string s
and an integer k
. The string s
consists of lowercase English letters. We consider a string beautiful if it obeys two conditions:
- The count of vowels in the string equals the count of consonants. (Vowels are 'a', 'e', 'i', 'o', 'u'; all other letters are consonants).
- The product of the number of vowels and consonants is divisible by
k
.
Our task is to find and return the number of non-empty substrings of s
that are beautiful.
A substring is defined as a contiguous segment of the string, and each individual character is also considered a substring of itself.
Intuition
To solve this problem, we can employ a brute-force approach by checking every possible substring of the string s
and determine whether it qualifies as beautiful according to the given criteria.
We initialize a count (ans
) that will keep track of the number of beautiful substrings found.
Starting with the first character, we iterate through the string s
using two nested loops: the outer loop sets the starting index of a substring, and the inner loop expands the substring by advancing the ending index.
For each possible substring, we maintain a vowel count. As we expand the substring by moving the ending index, we update the vowel count if the current character is a vowel.
Since we know the total length of the substring, we can find the number of consonants by subtracting the vowel count from the substring length.
We then check the first condition: whether the number of vowels equals the number of consonants. If this condition is not met, the substring cannot be beautiful and we continue with the next iteration.
If the first condition is satisfied, we compute the product of vowels and consonants and check if it is divisible by k
to fulfill the second condition.
Each time both conditions are met, we increment our count (ans
) as we've found a beautiful substring.
After checking all possible substrings, we return the count as our answer.
Here's a caveat: the above approach might not be optimal for strings with very large lengths, as the time complexity is O(n^2), which can cause timeouts on such inputs. However, the solution is quite straightforward and covers the basic brute-force approach to this problem.
Learn more about Prefix Sum patterns.
Solution Approach
The implementation of the solution follows a brute-force approach in which we consider every possible non-empty substring of the input string s
and check whether it is beautiful or not. Here is a walk-through of the algorithm:
- We initiate a
for
loop with the variablei
that will serve as the starting index of the substring we're evaluating. - For every
i
, we initiate an innerfor
loop with the variablej
which represents the ending index of the current substring. - A set
vs
contains all the vowel characters for constant time check of whether a character is a vowel or not. - We maintain a variable
vowels
to keep a count of the vowels in the current substring (fromi
toj
). - Inside the inner loop, we check if the character at the current
j
index is a vowel by checking its membership in thevs
set. If it is a vowel, we increment thevowels
count. - We then calculate the number of consonants in the current substring as the difference between the length of the substring (which is
j - i + 1
) and the number of vowels. - If the count of vowels equals the count of consonants (
vowels == consonants
), we check the second condition, which is whether their product is divisible byk
(vowels * consonants % k == 0
). If both conditions are fulfilled, the substring is beautiful and we increment our result counterans
. - We repeat this process for every
i
andj
until all substrings have been considered. - After the loops complete, the value of
ans
holds the total number of beautiful substrings and we return this value.
Let's examine the components of the code:
- Variables:
n
for the length of the input string,vs
for the set of vowels,ans
as the counter for beautiful substrings. - Data Structures: We utilize a
set
data structure (vs
) for fast lookup to check if a character is a vowel. - Loops: We use nested
for
loops to go through each possible substring.
Here's the code snippet that encapsulates the above logic:
class Solution:
def beautifulSubstrings(self, s: str, k: int) -> int:
n = len(s)
vs = set("aeiou")
ans = 0
for i in range(n):
vowels = 0
for j in range(i, n):
vowels += s[j] in vs
consonants = j - i + 1 - vowels
if vowels == consonants and vowels * consonants % k == 0:
ans += 1
return ans
The time complexity of this solution is O(n^2) because we examine each substring and the space complexity is O(1) since we only use a fixed amount of additional space.
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 walk through a small example to illustrate this solution. Suppose we have the string s = "azecb"
and we need to check for beautiful substrings with k = 2
.
-
We have the variable
i
ranging from 0 to 4 (for length ofs
which is 5). Let's examine the loops step by step wheni = 0
. -
We also define
vs
as a set containing'a', 'e', 'i', 'o', 'u'
to keep track of vowels. -
Starting with
i = 0
, we setvowels = 0
. This represents the starting index of our potential substring. Now, we will execute the inner loop withj
ranging also from 0 to 4:- For
j = 0
:- Our substring is
"a"
which has 1 vowel. - Since there are no consonants, this substring isn't beautiful, we move to the next iteration.
- Our substring is
- For
j = 1
:- Our substring is
"az"
. Now we have 1 vowel and 1 consonant. - The number of vowels equals the number of consonants and their product is 1 which is not divisible by 2. Not beautiful, continue.
- Our substring is
- For
j = 2
:- Substring
"aze"
. We have 2 vowels, 1 consonant. - Vowel and consonant counts do not match, continue.
- Substring
- For
j = 3
:- Substring
"azec"
. 2 vowels, 2 consonants. - Counts match, and their product is 4 which is divisible by 2. Beautiful substring! Increment
ans
to 1.
- Substring
- For
j = 4
:- Substring
"azecb"
. 2 vowels, 3 consonants. - Counts do not match, continue loop.
- Substring
- For
-
Repeat the same process, moving
i
from 1 to 4, each time resettingvowels
to 0, and checking each possible substring that begins at thati
.
After the loops complete, we have found all possible beautiful substrings and we have ans = 1
as our result, meaning there is 1 beautiful substring in "azecb" when k = 2
.
Hence, the beautifulSubstrings
function would return 1.
This example illustrated the process of using the brute-force approach in a smaller scenario. The actual function would perform similar steps for each index and count all such occurrences, giving us the total count of beautiful substrings.
Solution Implementation
1class Solution:
2 def beautifulSubstrings(self, input_string: str, divisor: int) -> int:
3 # Length of the input string.
4 string_length = len(input_string)
5
6 # Set containing vowels for quick lookup.
7 vowel_set = {"a", "e", "i", "o", "u"}
8
9 # Counter for the beautiful substrings.
10 beautiful_count = 0
11
12 # Loop through each character in the string as the starting point.
13 for start_index in range(string_length):
14 # Initialize the count of vowels found.
15 vowel_count = 0
16
17 # Explore all substrings that begin at start_index.
18 for end_index in range(start_index, string_length):
19 # Increment vowel count if the current character is a vowel.
20 vowel_count += input_string[end_index] in vowel_set
21
22 # Calculate number of consonants in the current substring.
23 consonant_count = (end_index - start_index + 1) - vowel_count
24
25 # Check if the substring is beautiful:
26 # The number of vowels and consonants must be equal and
27 # their product must be divisible by the divisor `k`.
28 if vowel_count == consonant_count and (vowel_count * consonant_count) % divisor == 0:
29 beautiful_count += 1
30
31 # Return the total count of beautiful substrings.
32 return beautiful_count
33
1class Solution {
2 public int beautifulSubstrings(String inputString, int divisor) {
3 int stringLength = inputString.length(); // Store the length of the input string.
4
5 // Initialize an array to mark vowels with '1' and consonants with '0'.
6 int[] vowelStatus = new int[26];
7 for (char vowel : "aeiou".toCharArray()) {
8 vowelStatus[vowel - 'a'] = 1; // Marking vowels in the array.
9 }
10
11 int totalCount = 0; // Initialize the count of beautiful substrings.
12
13 // Loop through each character of the string as a starting point.
14 for (int i = 0; i < stringLength; ++i) {
15 int vowelCount = 0; // Initialize the count of vowels encountered.
16
17 // Now check each substring starting from the current character.
18 for (int j = i; j < stringLength; ++j) {
19 // Increment the vowel count if a vowel is found.
20 vowelCount += vowelStatus[inputString.charAt(j) - 'a'];
21
22 // Calculate the number of consonants in the current substring.
23 int consonantCount = j - i + 1 - vowelCount;
24
25 // Check if the number of vowels and consonants are equal.
26 if (vowelCount == consonantCount) {
27 // Calculate the product of vowel and consonant counts.
28 int product = vowelCount * consonantCount;
29 // If the product is divisible by the divisor, increment the total count.
30 if (product % divisor == 0) {
31 totalCount++;
32 }
33 }
34 }
35 }
36
37 // Return the total count of beautiful substrings.
38 return totalCount;
39 }
40}
41
1class Solution {
2public:
3 int beautifulSubstrings(string str, int k) {
4 int n = str.size();
5 int vowelStatus[26] = {}; // Array to store the status of characters being vowel or not
6 string vowels = "aeiou"; // String containing all vowels
7
8 // Initialize the vowelStatus for vowels to 1
9 for (char c : vowels) {
10 vowelStatus[c - 'a'] = 1;
11 }
12
13 int answer = 0; // Variable to store the count of beautiful substrings
14
15 // Iterate through the string
16 for (int i = 0; i < n; ++i) {
17 int vowelCount = 0; // To track the count of vowels in the substring
18 // Explore all substrings starting at index i
19 for (int j = i; j < n; ++j) {
20 // Increment vowel count if current char is a vowel
21 vowelCount += vowelStatus[str[j] - 'a'];
22 // Get the count of consonants in the current substring
23 int consonantCount = (j - i + 1) - vowelCount;
24
25 // Check if the substring is beautiful according to the given conditions
26 if (vowelCount == consonantCount &&
27 (vowelCount * consonantCount) % k == 0) {
28 ++answer;
29 }
30 }
31 }
32
33 return answer; // Return the final count of beautiful substrings
34 }
35};
36
1function beautifulSubstrings(s: string, k: number): number {
2 const stringLength = s.length;
3 // Create an array to keep track of vowels (1) and consonants (0)
4 const vowelStatus: number[] = Array(26).fill(0);
5 // Identify and mark vowels in the vowelStatus array
6 for (const char of 'aeiou') {
7 vowelStatus[char.charCodeAt(0) - 'a'.charCodeAt(0)] = 1;
8 }
9
10 let beautifulCount = 0; // Counter for beautiful substrings
11 // Iterate over the string
12 for (let start = 0; start < stringLength; ++start) {
13 let vowelCount = 0;
14 // Explore all possible substrings starting from index 'start'
15 for (let end = start; end < stringLength; ++end) {
16 // Increment vowelCount if current character is a vowel
17 vowelCount += vowelStatus[s.charCodeAt(end) - 'a'.charCodeAt(0)];
18 // Calculate the number of consonants in the current substring
19 const consonantCount = (end - start + 1) - vowelCount;
20
21 // Check if the current substring is 'beautiful'
22 if (vowelCount === consonantCount && (vowelCount * consonantCount) % k === 0) {
23 beautifulCount++;
24 }
25 }
26 }
27
28 return beautifulCount; // Return the total count of beautiful substrings
29}
30
Time and Space Complexity
Time Complexity
The given code has a nested loop structure where the outer loop runs 'n' times (where n
is the length of string s
) and the inner loop runs from i
to n
. On each iteration of the inner loop, it performs constant-time operations. The worst-case time complexity happens when all characters from i
to n
are executed, which is the sum of an arithmetic series.
The time complexity can be calculated as:
- The outer loop runs
n
times. - The inner loop runs
n - i
times for everyi
.
So the total operations can be summed up as n + (n-1) + (n-2) + ... + 1
. This is equivalent to n * (n + 1) / 2
, which simplifies to O(n^2)
.
Space Complexity
The space complexity of the code is determined by the extra space used which is independent of the input size n
. Here, the variables used (vowels
, consonants
, vs
, and ans
) use constant space, and the set of vowels vs
contains a fixed number of elements irrespective of n
.
Thus, the space complexity is O(1)
, which means it uses constant space.
Learn more about how to find time and space complexity quickly using problem constraints.
Which data structure is used to implement recursion?
Recommended Readings
Prefix Sum The prefix sum is an incredibly powerful and straightforward technique Its primary goal is to allow for constant time range sum queries on an array What is Prefix Sum The prefix sum of an array at index i is the sum of all numbers from index 0 to i By
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
Want a Structured Path to Master System Design Too? Don’t Miss This!