Given an integer n
, return the number of strings of length n
that consist only of vowels (a
, e
, i
, o
, u
) and are lexicographically sorted.
A string s
is lexicographically sorted if for all valid i
, s[i]
is the same as or comes before s[i+1]
in the alphabet.
Example 1:
Input: n = 1
Output: 5
Explanation: The 5 sorted strings that consist of vowels only are ["a","e","i","o","u"].
Example 2:
Input: n = 2 Output: 15 Explanation: The 15 sorted strings that consist of vowels only are ["aa","ae","ai","ao","au","ee","ei","eo","eu","ii","io","iu","oo","ou","uu"]. Note that "ea" is not a valid string since 'e' comes after 'a' in the alphabet.
Example 3:
Input: n = 33 Output: 66045
Constraints:
1 <= n <= 50
This problem asks to find the number of lexicographically sorted strings of length n
using only vowels (a, e, i, o, u). We can solve this using dynamic programming or a mathematical approach.
This approach uses recursion with memoization to avoid redundant calculations.
Intuition:
We can build the solution recursively. For a string of length n
, the first character can be any vowel. Once the first character is chosen, the remaining n-1
characters must be chosen from vowels that are greater than or equal to the first character.
Algorithm:
n
is 0, there's one empty string (return 1).j
(from 'a' to 'u'), recursively count the number of sorted strings of length n-1
starting from vowel j
or greater. This sum represents the total number of sorted strings starting with vowel j
.Time Complexity: O(n5), because the depth of recursion is n and at each level there are 5 choices at most. In the worst case, it will visit each node in the recursion tree exactly once due to memoization. Space Complexity: O(n5) because of the memoization table (or call stack if memoization isn't used).
This approach iteratively builds the solution from smaller subproblems to larger ones.
Intuition:
Let's define f[i][j]
as the number of sorted strings of length i
ending with vowel j
(where 'a' is 0, 'e' is 1, etc.). We can build up this table row by row. The number of sorted strings of length i+1
ending in vowel k
is the sum of the number of sorted strings of length i
ending in vowels j
where j <=k
.
Algorithm:
f[1][j]
to 1 for all vowels j
(there's one string of length 1 for each vowel).i
from 2 to n
. For each length i
and vowel k
, calculate f[i][k]
as the sum of f[i-1][j]
for j <= k
.n
is the sum of f[n][j]
for all vowels j
.Time Complexity: O(n*5). The nested loop iterates through all the elements of the f
array, which has size n*5.
Space Complexity: O(n*5) to store the f
array. This can be reduced to O(5) by using only two rows, if we don't need to preserve previous levels.
The code examples in the provided markdown already demonstrate both approaches. Approach 1 is shown first (recursive with memoization), and Approach 2 is second (iterative). I won't repeat the code here as it's already well-presented in the question. The comments within the code also explain the logic of each step.
The iterative approach (Approach 2) is generally preferred because it's more efficient in terms of both time and space complexity (particularly if space is optimized to O(5) rather than O(n*5)). Recursive approaches can lead to stack overflow issues for very large values of n
, even with memoization. The iterative solution is less prone to this problem.