Description

給定一個只包含數字的字串 s,判斷是否能將其分割成兩個以上的子字串,使得分割後的數值嚴格遞減且相鄰差為 1。前導零是允許的(如 “050” 可以表示 50)。

Example:

Input: s = “0090089” Output: true(分割為 “0090”, “089” 即 90, 89)

Intuition#

核心思路:回溯嘗試第一段數值後,後續每段必須恰好比前一段小 1。

  • 第一段可以是任意長度(1 到 n-1),決定了起始值
  • 確定起始值後,後續每段的目標值是固定的(前一段 - 1)
  • 用回溯法嘗試不同的第一段切法
  • 注意:數字可能很大,需要用 Long 或 BigDecimal

Approaches#

⭐1: 回溯法#

  • 概念: 嘗試所有可能的第一段切法,確定起始值後遞迴驗證後續段是否能依序遞減 1
  • 時間複雜度: O(n^2) - n 為字串長度
  • 空間複雜度: O(n) - 遞迴深度
class Solution {
    fun splitString(s: String): Boolean {
        fun backtrack(start: Int, prev: Long, count: Int): Boolean {
            if (start == s.length) {
                return count >= 2
            }

            var num = 0L
            for (end in start until s.length) {
                num = num * 10 + (s[end] - '0')

                // 防止溢位
                if (num < 0) break

                if (count == 0) {
                    // 第一段,嘗試各種切法(不能取完整個字串)
                    if (end < s.length - 1 && backtrack(end + 1, num, 1)) {
                        return true
                    }
                } else {
                    if (num == prev - 1) {
                        if (backtrack(end + 1, num, count + 1)) {
                            return true
                        }
                    }
                    // 若 num 已經 >= prev,繼續加位數只會更大,可以剪枝
                    if (num >= prev) break
                }
            }

            return false
        }

        return backtrack(0, 0, 0)
    }
}

🔑 Takeaways#

  • Pattern: 字串分割 + 數值關係驗證 → 回溯嘗試切割點
  • 關鍵技巧: 第一段決定起始值後搜索空間大幅縮小;注意 Long 溢位問題;當目前數值已超過目標時可提前剪枝