Word Break
You are given a string s and a list of words called a dictionary. Return true if s can be cut into a sequence of dictionary words, with no letters left over. Words may be reused as many times as you like. The trick is to work left to right and keep one true-or-false answer per cut position : can the letters up to here be built out of dictionary words? A position is reachable when some earlier reachable position is followed by a dictionary word that ends right here.
Constraints
- 1 ≤ s.length ≤ 300
- 1 ≤ wordDict.length ≤ 1000
- 1 ≤ wordDicti.length ≤ 20
- s and the dictionary words contain only lowercase English letters
- All dictionary words are different from each other
Example
s = "catsand", wordDict = ["cat", "cats", "and", "sand"]trueExplanation "catsand" can be cut into "cat" + "sand", and both are in the dictionary.
In plain terms
- Dictionary
- The list of allowed words. Here it is only the words you are given, not a real English dictionary.
- Cut position
- A gap between letters, counted from 0 (before the first letter) up to the length of the string (after the last letter).
- Reachable
- A cut position is reachable when every letter before it has already been covered by whole dictionary words.
- Set
- A collection built for fast "is this in here?" checks, so looking a word up does not mean scanning the whole list.
Mark every cut position that whole words can reach
Only position 0 starts reachable — no letters covered yet.
What happens in this step
s = "catsand" dictionary = cat, cats, and, sand Every position is marked F except position 0, which is T. Position 0 means "no letters used so far", which is always possible.
Steps to visualize
- The row is the 8 cut positions of "catsand", from 0 (before the first letter) to 7 (after the last).
- T means that position is reachable using whole dictionary words; F means it is not.
- Position 0 starts as T because covering no letters at all needs no words.
- For each position, look back at every earlier position that is already T.
- If the letters between that earlier position and this one form a dictionary word, this position becomes T.
- The box marks the piece of the string being checked.
- The answer is the mark on the last position.
Walk through the code
Same walkthrough, now with the code. Press Next to move one step and watch which lines run.
Only position 0 starts reachable — no letters covered yet.
What happens in this step
s = "catsand" dictionary = cat, cats, and, sand Every position is marked F except position 0, which is T. Position 0 means "no letters used so far", which is always possible.
Solution
function wordBreak(s, wordDict) {
const words = new Set(wordDict);
const reachable = new Array(s.length + 1).fill(false);
reachable[0] = true;
for (let end = 1; end <= s.length; end += 1) {
for (let start = 0; start < end; start += 1) {
if (reachable[start] && words.has(s.slice(start, end))) {
reachable[end] = true;
break;
}
}
}
return reachable[s.length];
}- Time
- O(n^2 * k) where n is the string length and k the longest word
- Space
- O(n + total dictionary size)
Test cases
| Input | Expected | Covers |
|---|---|---|
s = "catsand", wordDict = ["cat", "cats", "and", "sand"] | true | example from the description |
s = "leetcode", wordDict = ["leet", "code"] | true | a clean split into exactly two words |
s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"] | false | a split that looks promising but leaves letters over |
s = "a", wordDict = ["a"] | true | smallest possible input |
s = "aaaaa", wordDict = ["b"] | false | no dictionary word appears in the string at all |
s = "applepenapple", wordDict = ["apple", "pen"] | true | the same word is reused |