Word Break
Master this topic with zero to advance depth.
Word Break
Given a string s and a dictionary of strings wordDict, return true if s can be segmented into a space-separated sequence of one or more dictionary words.
Note that the same word in the dictionary may be reused multiple times in the segmentation.
Visual Representation
s = "leetcode", wordDict = ["leet", "code"]
s[0:4] = "leet" (Found!)
s[4:8] = "code" (Found!)
Result: True
DP Strategy:
dp[i] is true if s[0...i] can be segmented.
dp[i] = true if EXISTS j < i such that dp[j] is true AND s[j...i] is in wordDict.Examples
Return true because "leetcode" can be segmented as "leet code".
Return true because "applepenapple" can be segmented as "apple pen apple".
Level I: Brute Force (Recursion)
Intuition
Try every possible prefix of the string. If a prefix is in the dictionary, recursively check if the remaining suffix can also be segmented into dictionary words.
Thought Process
- For a string
s, iterateifrom 1 tolen(s). - If
s[0:i]is inwordDict:- Recursively call
canBreak(s[i:]).
- Recursively call
- Base case: If the remaining string is empty, return
true.
Detailed Dry Run
s = "abc", wordDict = ["a", "bc"]
- prefix "a" in Dict? Yes. Recurse on "bc".
- prefix "b" in Dict? No.
- prefix "bc" in Dict? Yes. Recurse on "".
- Base case "" -> True.
Level II: Memoization (Top-Down DP)
Intuition
The brute-force recursion checks all prefixes and re-explores the same suffixes repeatedly. By caching results for each starting index, we eliminate redundant work.
Visual
s = "leetcode", dict = ["leet", "code"]
memo[0]=T -> "leet" matches -> memo[4]=?
memo[4]=T -> "code" matches -> memo[8]=TDetailed Dry Run
s="catsdog", dict=["cats","dog"]. memo[0]: "cats" -> memo[4]. memo[4]: "dog" -> memo[7]=T. Cached!
⚠️ Common Pitfalls & Tips
Use a Set for dictionary lookups (O(1)) instead of a list (O(L)). Without this, the overall complexity worsens significantly.
Level III: Dynamic Programming (Bottom-Up)
Intuition
We check every possible prefix of the string. If a prefix s[0...j] can be segmented (indicated by dp[j] = true), we then check if the remaining substring s[j...i] exists in the dictionary.
Thought Process
- Initialize
dparray of sizen+1withfalse. dp[0] = true(empty string is always valid).- For
ifrom 1 ton:- For
jfrom 0 toi-1:- If
dp[j]is true ANDs.substring(j, i)is inwordDict:- Set
dp[i] = trueandbreak.
- Set
- If
- For
- Return
dp[n].
Detailed Dry Run
s = "leetcode", dict = ["leet", "code"]
| i | j | Substring | dp[j] | Solution |
|---|---|---|---|---|
| 0 | - | - | T | Base Case |
| 4 | 0 | "leet" | T | dp[4] = True |
| 8 | 4 | "code" | T | dp[8] = True |
⚠️ Common Pitfalls & Tips
Be careful with substring indexing (inclusive start, exclusive end). Using a Set for dictionary lookup improves lookup time from O(W) to O(1) average.
Found an issue or have a suggestion?
Help us improve! Report bugs or suggest new features on our Telegram group.