Description

給定 low, high, zero, one。每步可以在字串末尾加上 zero 個 ‘0’ 或 one 個 ‘1’。求長度在 [low, high] 之間的「好字串」有多少個,結果對 10^9 + 7 取模。

Example:

Input: low = 3, high = 3, zero = 1, one = 1 Output: 8

Intuition#

Climbing Stairs 的變體 — 每步可以跳 zero 格或 one 格,求到達 [low, high] 範圍內任何位置的方法總數。

  • dp[i] = 建出長度恰好為 i 的字串的方法數
  • 狀態轉移:dp[i] = dp[i - zero] + dp[i - one]
  • 最終答案:sum(dp[low..high])

Approaches#

1: Top-Down Memoization#

  • 概念: 遞迴嘗試加 zero 或 one 個字元,記憶化
  • 時間複雜度: O(high)
  • 空間複雜度: O(high)
class Solution {
    fun countGoodStrings(low: Int, high: Int, zero: Int, one: Int): Int {
        val MOD = 1_000_000_007
        val memo = IntArray(high + 1) { -1 }
        fun dp(len: Int): Int {
            if (len > high) return 0
            if (memo[len] != -1) return memo[len]
            var res = if (len >= low) 1 else 0
            res = (res + dp(len + zero)) % MOD
            res = (res + dp(len + one)) % MOD
            memo[len] = res
            return res
        }
        return dp(0)
    }
}

⭐2: Bottom-Up DP#

  • 概念: 從長度 0 到 high 逐步計算方法數,累加 [low, high] 範圍的答案
  • 時間複雜度: O(high)
  • 空間複雜度: O(high)
class Solution {
    fun countGoodStrings(low: Int, high: Int, zero: Int, one: Int): Int {
        val MOD = 1_000_000_007
        val dp = IntArray(high + 1)
        dp[0] = 1
        var ans = 0
        for (i in 1..high) {
            if (i >= zero) dp[i] = (dp[i] + dp[i - zero]) % MOD
            if (i >= one) dp[i] = (dp[i] + dp[i - one]) % MOD
            if (i in low..high) ans = (ans + dp[i]) % MOD
        }
        return ans
    }
}

🔑 Takeaways#

  • Pattern: Climbing Stairs 推廣 — 步長從固定的 1/2 變成可變的 zero/one
  • 關鍵技巧: 不要被字串描述迷惑,本質上就是「幾種方式走到某個長度」的計數問題