class Solution:
def countVowelPermutation(self, n: int) -> int:
MOD = 10 ** 9 + 7
# a, e, i, o, u
dp = [1] * 5
for _ in range(1, n):
a = (dp[1] + dp[2] + dp[4]) % MOD
e = (dp[0] + dp[2]) % MOD
i = (dp[1] + dp[3]) % MOD
o = dp[2] % MOD
u = (dp[2] + dp[3]) % MOD
dp = [a, e, i, o, u]
return sum(dp) % MOD
class Solution {
public:
int countVowelPermutation(int n) {
const int MOD = 1e9 + 7;
long a = 1, e = 1, i = 1, o = 1, u = 1;
for (int k = 1; k < n; ++k) {
long na = (e + i + u) % MOD;
long ne = (a + i) % MOD;
long ni = (e + o) % MOD;
long no = i % MOD;
long nu = (i + o) % MOD;
a = na; e = ne; i = ni; o = no; u = nu;
}
return (a + e + i + o + u) % MOD;
}
};
class Solution {
public int countVowelPermutation(int n) {
int MOD = 1000000007;
long a = 1, e = 1, i = 1, o = 1, u = 1;
for (int k = 1; k < n; ++k) {
long na = (e + i + u) % MOD;
long ne = (a + i) % MOD;
long ni = (e + o) % MOD;
long no = i % MOD;
long nu = (i + o) % MOD;
a = na;
e = ne;
i = ni;
o = no;
u = nu;
}
return (int)((a + e + i + o + u) % MOD);
}
}
var countVowelPermutation = function(n) {
const MOD = 1e9 + 7;
let a = 1, e = 1, i = 1, o = 1, u = 1;
for (let k = 1; k < n; ++k) {
let na = (e + i + u) % MOD;
let ne = (a + i) % MOD;
let ni = (e + o) % MOD;
let no = i % MOD;
let nu = (i + o) % MOD;
a = na; e = ne; i = ni; o = no; u = nu;
}
return (a + e + i + o + u) % MOD;
};
Given an integer n
, you are asked to return the number of strings of length n
that can be formed using only the vowels 'a'
, 'e'
, 'i'
, 'o'
, and 'u'
under the following rules:
'a'
may only be followed by 'e'
.'e'
may only be followed by 'a'
or 'i'
.'i'
may not be followed by another 'i'
.'o'
may only be followed by 'i'
or 'u'
.'u'
may only be followed by 'a'
.10^9 + 7
.
The task is to count all possible valid strings of length n
that can be constructed under these constraints.
At first glance, this problem appears to involve generating all possible vowel strings of length n
and counting those that obey the transition rules. However, as n
increases, the number of possible strings grows exponentially, making brute-force enumeration infeasible.
To optimize, we realize that the restrictions on which vowel can follow which form a set of transitions. This suggests a dynamic programming approach, where we keep track of the number of valid strings ending with each vowel at each position in the string.
Instead of tracking every possible string, we only need to track counts for each vowel at each length, updating these counts based on the allowed transitions.
We use dynamic programming to keep track of how many strings of a given length end with each vowel. The idea is to build up the solution one character at a time, using the transition rules.
dp[vowel]
represent the number of ways to form a string of the current length ending with vowel
.
dp = [1, 1, 1, 1, 1]
for 'a'
, 'e'
, 'i'
, 'o'
, and 'u'
respectively.
'a'
can follow 'e'
, 'i'
, 'u'
'e'
can follow 'a'
, 'i'
'i'
can follow 'e'
, 'o'
'o'
can follow 'i'
'u'
can follow 'i'
, 'o'
n
, update the counts for each vowel using the previous counts and the above transitions.
n
.
10^9 + 7
at each step to avoid overflow.
This approach ensures we only ever store five numbers at a time, making it both time and space efficient.
Let's walk through the case where n = 2
.
a = 1
, e = 1
, i = 1
, o = 1
, u = 1
a
: can be after e
, i
, u
→ a = e + i + u = 1 + 1 + 1 = 3
e
: can be after a
, i
→ e = a + i = 1 + 1 = 2
i
: can be after e
, o
→ i = e + o = 1 + 1 = 2
o
: can be after i
→ o = i = 1
u
: can be after i
, o
→ u = i + o = 1 + 1 = 2
3 + 2 + 2 + 1 + 2 = 10
So, for n = 2
, there are 10 valid vowel permutations.
n
using 5 vowels, i.e., 5^n
possibilities.
O(5^n)
O(n)
for recursion stack (if using backtracking)
n
positions, we do constant (5) work.
O(n)
O(1)
(since we only keep counts for 5 vowels)
The DP approach is exponentially faster and uses minimal space.
The key insight is to recognize that this problem is about counting, not generating, valid strings. By modeling the allowed transitions with dynamic programming, we avoid the exponential blowup of brute-force methods. The solution is elegant because it leverages the small, fixed set of states (the vowels) and the predictable transitions between them, resulting in a very efficient algorithm.