# 刷題筆記 - LeetCode 139. Word Break
https://leetcode.com/problems/word-break
## Thoughts
給一個 string `s`, 看他這個 s 能不能被 `wordDict` 給的字組合而成
Example 1:
>Input: s = "leetcode", wordDict = ["leet","code"]
Output: true
Explanation: Return true because "leetcode" can be segmented as "leet code".
Example 2:
>Input: s = "applepenapple", wordDict = ["apple","pen"]
Output: true
Explanation: Return true because "applepenapple" can be segmented as "apple pen apple".
Note that you are allowed to reuse a dictionary word.
Example 3:
>Input: s = "catsandog", wordDict = ["cats","dog","sand","and","cat"]
Output: false
\
從 Example 3 可看出,`s` 要可以被 wordDict 中的字拆分
如果更改一下 Test case
>Input: s = "catsanddog", wordDict = ["cats","dog","sand","and","cat"]
Output: true
`s` 可以分成 `cats`, `and`, `dog`,這個組合全都有出現在 `wordDict` 中,因此回傳是 `true`
## Solution 1 - DP, bottom-up
### Logic
拆分問題 -
> `s[0:i]` 是不是 wordDict 中出現的字?
> 是的話,`s[i:]` 能不能繼續拆?
假設使用 DP,先建立 DP array
```
dp = [False] * (len(s) + 1)
```
利用每個 `dp[i]` 來記錄 `s[0:i]` 是不是存在 wordDict 中
\
為什麼 dp array 長度是設定成 `len(s) + 1`?
因為如果 `len(s) == 0` 那一定可以被拆解 (`dp[0]=0`)
\
假設 `s` 長度為 8,整個遍歷的起始會是
```
0 1 2 3 4 5 6 7 8
↑
i
```
從 1 開始,檢查`s[0:1]` 是否在字典中
遍歷 `s`,假設走到的位置 `i`,可以被正確拆分的話
=> 代表 `i` 之前的 substring,也是可以被正確拆分
那再去看 substring,就是一個重複的問題
```
0 1 2 3 4 5 6 7 8
↑ ↑
j i
```
如果 `i` 走到 7
那就必須檢查 `s[0:7]` 是否能被正確拆分
**代表必須再一個迴圈來檢查 substring** (上圖 `j`)
而我們使用 `dp[i]` 來儲存 `s[0:i]` 是否存在字典中
那 substring 就可以被拆成:`s[0:j]` & `s[j:i]`
所以假設 `s[0:j]` 在字典中 (也就是 `dp[j]==True`) ,那需要確認的就是 `s[j:i]` 是不是在字典中,是的話那 `dp[i]` 就也應該設成 True
<br>
### Code
```python!=
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
wordSet = set(wordDict)
dp = [False] * (len(s) + 1)
dp[0] = True
for i in range(1,len(s) + 1):
for j in range(0, i):
if dp[j] and s[j:i] in wordSet:
dp[i] = True
break
return dp[len(s)]
```
line 7: 從 1 開始遍歷,到 len(s) (dp array 長度是 len(s)+1)
line 8: 第二層迴圈去看 `s[:i]` 的 substring,確認是不是能被拆分。因為先前已經使用 `dp[i]` 來儲存該位置是否確定可被字典中的字所組合,所以可以用 `dp[j]` 來判斷,而剩下的字串就是 `s[j:i]`
line 11: 如果確定至少有一個組合是可被字典拆分,就不用再繼續第二層迴圈,直接 break
最後回傳 `dp[len(s)]` 就是答案
### Time & Space Complexity
- Time: O(n^2)
- n: length of the string`s`
- Space: O(n + k)
- n: 建了一個 `len(s)+1` 的 DP array
- k: 建立 `wordSet`,k 表示 wordDict 字的數量
### Optimization
第二層迴圈 `for j in range(0:i):` 可以不用跑完所有的 `0 ~ i`
假設字典裡面最長的單字長度為 `m`,那就不用檢查比 `m` 還長的 `s[j:i]`,因為一定不存在於字典中
## Solution 2 - Recursion
(working)