Leetcode 139: Word Break
Given a string s and a dictionary of words wordDict, return true if s can be segmented into a space-separated sequence of one or more words from the dictionary. The same word in the dictionary can be reused multiple times.
Problem
Approach
Steps
Complexity
Input: The input consists of a string s and a list of words wordDict.
Example: Input: s = 'applepie', wordDict = ['apple', 'pie']
Constraints:
• 1 <= s.length <= 300
• 1 <= wordDict.length <= 1000
• 1 <= wordDict[i].length <= 20
• s and wordDict[i] consist of only lowercase English letters.
• All strings in wordDict are unique.
Output: The output is a boolean value indicating whether the string s can be segmented into words from the dictionary wordDict.
Example: Output: true
Constraints:
• The output is true if s can be segmented into valid words from wordDict, otherwise false.
Goal: The goal is to determine if the string s can be segmented into words from the dictionary wordDict.
Steps:
• 1. Use dynamic programming to check if the string can be segmented.
• 2. Iterate through the string s and use a memoization technique to avoid redundant calculations.
• 3. For each substring of s, check if it exists in the wordDict and recursively check if the rest of the string can be segmented.
Goal: Ensure the solution handles strings of varying lengths and wordDict with up to 1000 words efficiently.
Steps:
• The solution must handle strings with lengths up to 300.
• The solution must handle large wordDict sizes, up to 1000 unique words.
Assumptions:
• The wordDict contains only lowercase English words.
• The input string s and words in wordDict are non-empty and consist of lowercase letters only.
• Input: Input: s = 'applepie', wordDict = ['apple', 'pie']
• Explanation: The string 'applepie' can be split into 'apple' and 'pie', both of which are present in the dictionary, hence the answer is true.
Approach: The approach involves dynamic programming and memoization to determine if the string can be segmented into words from the dictionary.
Observations:
• This is a classical dynamic programming problem where we try to break the string down into smaller valid substrings.
• We will need to track which parts of the string have already been checked to avoid redundant work.
Steps:
• 1. Initialize a memoization array to store whether each substring of s can be segmented.
• 2. Check each possible substring of s and recursively verify if the rest of the string can be segmented into words from wordDict.
Empty Inputs:
• If s is an empty string, return true since it trivially satisfies the condition.
Large Inputs:
• For large inputs, the solution must efficiently handle strings up to 300 characters and wordDict with up to 1000 entries.
Special Values:
• Consider strings where no valid segmentation is possible, such as 'banana' with dictionary ['apple', 'pie'].
Constraints:
• Ensure that the solution uses dynamic programming or other efficient methods to handle large inputs within the given constraints.
vector<int> memo;
bool wordBreak(string s, vector<string>& dict) {
map<string, bool> mp;
for(string d: dict)
mp[d] = true;
memo.resize(s.size(), -1);
return bt(s, 0, mp);
}
bool bt(string s, int idx, map<string, bool> &mp) {
if(idx == s.size()) return true;
if(memo[idx] != -1) return memo[idx];
for(int i = idx; i < s.size(); i++) {
if(mp.count(s.substr(idx, i - idx + 1)) && bt(s, i + 1, mp))
return memo[idx] = true;
}
return memo[idx] = false;
}
1 : Variable Declaration
vector<int> memo;
A vector `memo` is declared to store the results of subproblems for dynamic programming. It will store -1 for uncalculated indices and a boolean value for each index after it is processed.
2 : Function Definition (wordBreak)
bool wordBreak(string s, vector<string>& dict) {
This is the main function `wordBreak`, which checks if the string `s` can be segmented using words from the dictionary `dict`.
3 : Dictionary Setup
map<string, bool> mp;
A map `mp` is created to store the dictionary words as keys with their values set to `true`, for efficient look-up during segmentation.
4 : Populate Dictionary Map
for(string d: dict)
This loop iterates over the dictionary `dict` and adds each word to the map `mp`.
5 : Set Map Values
mp[d] = true;
The map `mp` is populated with each word from the dictionary as the key and `true` as the value to indicate that the word exists in the dictionary.
6 : Resize Memoization Vector
memo.resize(s.size(), -1);
The `memo` vector is resized to the size of the string `s` and initialized with -1 to indicate that no positions have been processed yet.
7 : Recursive Call (bt)
return bt(s, 0, mp);
The `bt` function is called with the initial index `0` and the map `mp` to start checking if the string `s` can be segmented from the beginning.
8 : Helper Function Definition (bt)
bool bt(string s, int idx, map<string, bool> &mp) {
This is the definition of the helper function `bt`, which recursively checks if the string can be segmented from index `idx` onward.
9 : Base Case (end of string)
if(idx == s.size()) return true;
If the current index `idx` is equal to the length of the string `s`, it means the entire string has been successfully segmented, so return `true`.
10 : Check Memoization
if(memo[idx] != -1) return memo[idx];
If the value at `memo[idx]` is not -1, it means this subproblem has already been solved, so return the stored result.
11 : Start Loop (check substrings)
for(int i = idx; i < s.size(); i++) {
This loop checks all possible substrings starting from the current index `idx`.
12 : Check Substring in Dictionary
if(mp.count(s.substr(idx, i - idx + 1)) && bt(s, i + 1, mp))
If the substring from `idx` to `i` exists in the dictionary (checked via `mp.count`) and the remaining part of the string can be segmented (recursively checked using `bt`), proceed.
13 : Memoize True
return memo[idx] = true;
If a valid segmentation is found, store the result as `true` in `memo[idx]` and return `true`.
14 : Memoize False
return memo[idx] = false;
If no valid segmentation is found after checking all substrings, store the result as `false` in `memo[idx]` and return `false`.
Best Case: O(n^2), where n is the length of the string. This occurs when we have to check all substrings of the string.
Average Case: O(n^2) in most cases.
Worst Case: O(n^2) in the worst case, where n is the length of the string.
Description: In the worst case, each substring of s must be checked, leading to a time complexity of O(n^2).
Best Case: O(n), where n is the length of the string.
Worst Case: O(n), where n is the length of the string. This is due to the space needed for memoization.
Description: The space complexity is O(n) due to the storage needed for the memoization array.
LeetCode Solutions Library / DSA Sheets / Course Catalog |
---|
comments powered by Disqus