Problem Description
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.
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
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.
Difficulty: Medium
Tags: array, hash table, string, dynamic programming, trie, memoization
Rating: 95.51%
The Dynamic Programming Solution
Let’s break down the solution step by step:
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
# Convert wordDict to a set for O(1) lookups
words = set(wordDict)
# dp[i] represents if s[0:i] can be segmented using dictionary words
dp = [False] * (len(s) + 1)
# Empty string is always valid
dp[0] = True
# For each end position in the string
for r in range(1, len(s) + 1):
# Try all possible starting positions for the last word
for l in range(r):
# If we can form s[0:l] AND s[l:r] is in dictionary
if dp[l] and s[l:r] in words:
dp[r] = True
break
return dp[-1]
How the Algorithm Works
Let’s walk through an example with s = “leetcode” and wordDict = [“leet”, “code”]:
- Initialize dp array: [True, False, False, False, False, False, False, False, False]
- For r = 1 (“l”):
- Check substrings: “l” → not in dictionary
- dp[1] remains False
- For r = 4 (“leet”):
- Check “leet” → in dictionary and dp[0] is True
- dp[4] becomes True
- For r = 8 (“leetcode”):
- When l = 4, we find that:
- dp[4] is True (we can form “leet”)
- “code” is in dictionary
- Therefore, dp[8] becomes True
- When l = 4, we find that:
Complexity Analysis
-
Time Complexity:
- We have a nested loop structure
- Outer loop runs n times
- Inner loop can run up to current position
- String slicing and set lookup are O(1)
-
Space Complexity:
- We only need the dp array of size n+1
- The word set takes additional space but is bounded by the dictionary size
Common Pitfalls and Optimization Tips
- Converting to Set: Always convert the wordDict to a set for O(1) lookups. Using a list would result in O(n) lookups.
- Breaking Early: The
break
statement is crucial for efficiency - once we find a valid segmentation for a position, we don’t need to check other possibilities. - Empty String Case: Handling the empty string case by setting dp[0] = True is essential for the algorithm to work correctly.
Additional Notes
- This solution is particularly efficient for shorter strings and moderate-sized dictionaries
- For very large strings or dictionaries, a trie-based solution might be more appropriate