Medium草稿★★★★★O(n^2 * m) 時間 · O(n) 空間
Dynamic ProgrammingStringHash TableTrie
Patterns🌴 字典樹📈 線性 DP・Kadane#️⃣ 雜湊
尚未複習過

解法已隱藏 — 先讀題目敘述、自己想想看,再點上方按鈕揭曉。

139Word Break1-D DPMedium1-D DP

給定一個字串 s 和一個字典 wordDict,判斷 s 是否可以被拆分為一個或多個字典中的單詞。字典中的單詞可以重複使用。

Example:

Input: s = “leetcode”, wordDict = [“leet”,“code”] Output: true

Intuition

TIP

dp[i] 代表 s[0..i-1] 是否可被拆分,對每個位置回頭找是否存在有效的切割點。

Approaches

1. Top-Down Memoization — O(n^2 * m) / O(n)
  • Idea: 遞迴嘗試每個前綴是否在字典中,若是則遞迴處理剩餘部分
  • Time: O(n^2 * m) — n 為字串長度,m 為子字串比較時間
  • Space: O(n)
class Solution {
    fun wordBreak(s: String, wordDict: List<String>): Boolean {
        val wordSet = wordDict.toHashSet()
        val memo = HashMap<Int, Boolean>()
        fun dp(start: Int): Boolean {
            if (start == s.length) return true
            memo[start]?.let { return it }
            for (end in start + 1..s.length) {
                if (s.substring(start, end) in wordSet && dp(end)) {
                    memo[start] = true
                    return true
                }
            }
            memo[start] = false
            return false
        }
        return dp(0)
    }
}
⭐ 2. Bottom-Up DP — O(n^2 * m) / O(n)
  • Idea: dp[i] 表示 s[0..i-1] 是否可拆分,對每個位置 i 檢查所有可能的最後一個單詞
  • Time: O(n^2 * m)
  • Space: O(n)
class Solution {
    fun wordBreak(s: String, wordDict: List<String>): Boolean {
        val wordSet = wordDict.toHashSet()
        val dp = BooleanArray(s.length + 1)
        dp[0] = true
        for (i in 1..s.length) {
            for (j in 0 until i) {
                if (dp[j] && s.substring(j, i) in wordSet) {
                    dp[i] = true
                    break
                }
            }
        }
        return dp[s.length]
    }
}

🔑 Takeaways