Description
給定一個整數
n,計算長度為n的字串中,由母音字母 (a, e, i, o, u) 組成且符合以下規則的字串數量(結果對 10^9+7 取餘):
a後面只能接ee後面只能接a或ii後面不能接io後面只能接i或uu後面只能接a
Example:
Input: n = 2 Output: 10
Intuition#
狀態轉移 DP:根據每個母音的後繼規則,逐步累加長度為 n 的合法排列數。
- 以每個母音結尾的字串數量為狀態
- 根據規則反向推導:哪些母音可以接在某母音之前
a可以被e,i,u接在前面;e可以被a,i接在前面…
Approaches#
1: 2D DP#
- 概念:
dp[i][v]表示長度為i、以母音v結尾的字串數。根據轉移規則從dp[i-1]推導。 - 時間複雜度:
O(n) - 空間複雜度:
O(n)
class Solution {
fun countVowelPermutation(n: Int): Int {
val MOD = 1_000_000_007L
val dp = Array(n) { LongArray(5) }
// 0=a, 1=e, 2=i, 3=o, 4=u
for (j in 0..4) dp[0][j] = 1
for (i in 1 until n) {
dp[i][0] = (dp[i - 1][1] + dp[i - 1][2] + dp[i - 1][4]) % MOD
dp[i][1] = (dp[i - 1][0] + dp[i - 1][2]) % MOD
dp[i][2] = (dp[i - 1][1] + dp[i - 1][3]) % MOD
dp[i][3] = dp[i - 1][2] % MOD
dp[i][4] = (dp[i - 1][2] + dp[i - 1][3]) % MOD
}
return (dp[n - 1].sum() % MOD).toInt()
}
}⭐2: 空間優化 DP#
- 概念: 只需要五個變數記錄上一輪各母音的數量。
- 時間複雜度:
O(n) - 空間複雜度:
O(1)
class Solution {
fun countVowelPermutation(n: Int): Int {
val MOD = 1_000_000_007L
var a = 1L; var e = 1L; var i = 1L; var o = 1L; var u = 1L
repeat(n - 1) {
val na = (e + i + u) % MOD
val ne = (a + i) % MOD
val ni = (e + o) % MOD
val no = i % MOD
val nu = (i + o) % MOD
a = na; e = ne; i = ni; o = no; u = nu
}
return ((a + e + i + o + u) % MOD).toInt()
}
}🔑 Takeaways#
- Pattern: 狀態機 DP — 每個狀態(母音)有固定的轉移規則
- 關鍵技巧: 反向思考轉移關係(誰可以轉移到當前狀態)比正向更直觀;常數個狀態可以壓縮至 O(1) 空間