139. Word Break
- Goal: Determine if a string
s
can be segmented into a space-separated sequence of one or more dictionary words fromwordDict
. - Constraints:
1 <= s.length <= 300
1 <= wordDict.length <= 1000
1 <= wordDict[i].length <= 20
s
andwordDict[i]
consist of only lowercase English letters.- All the strings of
wordDict
are unique.
Understanding the Problem
Given a string s
and a list of words
wordDict
, we need to check if s
can be broken
down into a sequence of one or more words from wordDict
.
The same word from the dictionary can be used multiple times.
Analogy: Puzzle Pieces
- String
s
: Think ofs
as a long puzzle strip. - Dictionary Words (
wordDict
): Each word is like a puzzle piece. - Goal: Use the puzzle pieces to completely cover the puzzle strip without gaps or overlaps.
Example
1 | Input: s = "leetcode", wordDict = ["leet", "code"] |
Quick Steps for Solving the Problem
1. Use Dynamic Programming (DP)
- Define Subproblems: Determine if substrings
s[0..i]
can be segmented usingwordDict
. - DP Array (
dp
): Create an arraydp
wheredp[i]
istrue
ifs[0..i-1]
can be segmented.
2. Initialise the DP Array
- Set
dp[0] = true
since an empty string can always be segmented.
3. Fill the DP Array
- For each position
i
from1
tos.length
:- For each
j
frommax(0, i - maxWordLength)
toi
:- If
dp[j]
istrue
ands[j..i-1]
is inwordDict
, setdp[i] = true
and break the inner loop.
- If
- For each
maxWordLength
is a way to optimise - without it, you inner loop can go over longest length from dict.
4. Return the Result
- Return
dp[s.length]
, which indicates whethers
can be segmented.
Step-by-Step Implementation
1. Initialise Variables
N
: Length of the strings
.wordSet
: ASet
containing words fromwordDict
for efficient lookup.maxWordLength
: The maximum length of words inwordDict
(denoted asL
).
2. Compute
maxWordLength
1 | let maxWordLength = 0; |
3. Initialise DP Array
1 | const dp = new Array(N + 1).fill(false); |
4. Fill the DP Array Using DP Algorithm
1 | for (let i = 1; i <= N; i++) { |
e.g. if we dont break: 1
2
3
4
5
6
7s[0:1]: c
s[0:2]: ca
s[1:2]: a
s[0:3]: cat
s[1:3]: at // As you can see its still to look up, even tho the current dp[j] is already set to true. To be more efficient. Even there is "ca", "at" in the list, this will always be true regardless.
s[2:3]: t
s[0:4]: cats
if we break: 1
2
3
4
5
6
7
8
9
10s[0:1]: c
s[0:2]: ca
s[1:2]: a
s[0:3]: cat
break.
s[0:4]: cats
s[1:5]: atsa
s[2:5]: tsa
s[3:5]: sa
s[4:5]: a
5. Return the Result
1 | return dp[N]; |
JavaScript Implementation
1 | /** |
Time & space complexity expressions:
- O(N * L):
- N: The length of the input string
s
. - L: The maximum length of the words in the
dictionary
wordDict
.
- N: The length of the input string
- O(N + W):
- N: The length of the input string
s
. - W: The total number of words in
wordDict
.
- N: The length of the input string
Detailed Explanation
Understanding the DP Array
(dp
)
dp[i]
: A boolean value indicating whether the substrings[0..i-1]
can be segmented into words fromwordDict
.
Why Use maxWordLength
?
- Purpose: To optimize the inner loop by limiting the
range of
j
. - Effect: Reduces the number of iterations, improving the time complexity.
- Definition of
L
: The maximum length of words inwordDict
.
Time and Space Complexity
- Time Complexity: O(N * L), where:
- N: Length of the string
s
. - L: Maximum length of the words in
wordDict
.
- N: Length of the string
- Space Complexity: O(N + W), where:
- N: Length of the string
s
(for thedp
array). - W: Total number of words in
wordDict
(for thewordSet
).
- N: Length of the string
Example Walkthrough
Example:
s = "leetcode"
,
wordDict = ["leet", "code"]
Initialisation
N = 8
(length of "leetcode")maxWordLength = 4
(max length of words inwordDict
)dp = [true, false, false, false, false, false, false, false, false]
Filling the DP Array
- i = 4
- j from max(0, 4 - 4) = 0 to 3
- j = 0:
s[0:4] = "leet"
is inwordDict
, anddp[0]
istrue
. - Set
dp[4] = true
and break.
- j = 0:
- j from max(0, 4 - 4) = 0 to 3
- i = 8
- j from max(0, 8 - 4) = 4 to 7
- j = 4:
s[4:8] = "code"
is inwordDict
, anddp[4]
istrue
. - Set
dp[8] = true
and break.
- j = 4:
- j from max(0, 8 - 4) = 4 to 7
Final DP Array
dp = [true, false, false, false, true, false, false, false, true]
Result
Since dp[8] = true
, the string "leetcode" can be
segmented.
Additional Notes
Why Use break
in
the Inner Loop?
- Efficiency: Once we find a valid segmentation at
position
i
, there's no need to check further. - Early Termination: Improves performance by reducing unnecessary iterations.
Handling Edge Cases
- Empty String:
dp[0] = true
ensures that we can handle an empty string. - Words Longer Than
maxWordLength
: The inner loop accounts for this by starting frommax(0, i - maxWordLength)
.
Summary
- Dynamic Programming: An effective approach for solving the Word Break problem by breaking it down into subproblems.
- Optimization with
maxWordLength
: Limits the inner loop iterations, improving time complexity. - Time Complexity: \(O(N * L)\), efficient for the given constraints; \(L\): The maximum length of the words in the dictionary wordDict.
- Space Complexity: \(O(N + W)\), acceptable given the problem size; \(W\): The total number of words in wordDict.
Conclusion
By understanding the dynamics of the problem and applying dynamic programming with appropriate optimisations, we can efficiently solve the Word Break problem. Remember to:
- Define the DP State Clearly: Know what each element in your DP array represents.
- Optimise Loops: Use variables like
maxWordLength
to reduce unnecessary computations. - Practice: Apply similar strategies to other DP problems to reinforce your understanding.
Reminder from GPT:
Dynamic Programming (DP) is a powerful tool for solving problems with overlapping subproblems and optimal substructure, like the Word Break problem. By meticulously defining subproblems, optimising iterations, and utilising appropriate data structures, you can craft efficient and effective solutions.
- Define Clear States: Understand what each state in
your DP array represents. Optimise Loops: Use constraints like
maxWordLength
to limit unnecessary computations. - Early Exits: Implement break statements where applicable to enhance performance.
- Keep practicing similar DP problems to reinforce these concepts, and you'll gain deeper intuition and mastery over such algorithms.
Practice Problems
To strengthen your understanding, consider practicing these related problems:
- LeetCode 140: Word Break II
- LeetCode 472: Concatenated Words