Problem description:

Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words. You may assume the dictionary does not contain duplicate words.

For example, given
s = “leetcode”,
dict = [“leet”, “code”].

Return true because “leetcode” can be segmented as “leet code”.

UPDATE (2017/1/4):
The wordDict parameter had been changed to a list of strings (instead of a set of strings). Please reload the code definition to get the latest changes.

Solution:

The idea for the solution is that a given string s can be divided into two substring s1 and s2. If s1 and s2 both fulfill the requirement, then complete the problem. Let’s create a dp array, which dp[i]= 1 means we are able to find match in dictionary.

As we just said, the original string can treat as substrings. Therefore, we can use a two pointer to check whether if there’s matches in a string.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
# DP
# subproblem answer lead to final one
# if we can find x is s[i+1:] in wordDict after completed s[0:i]
# then return true
dp = [False] * (len(s)+1)
dp[0] = True
for i in range(1, len(s)+1):
for j in range(i):
if dp[j] and s[j:i] in wordDict:
dp[i] = True
break
return dp[-1]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Solution {
public:
//https://leetcode.com/problems/word-break/discuss/43814/C++-Dynamic-Programming-simple-and-fast-solution-(4ms)-with-optimization
bool wordBreak(string s, vector<string>& wordDict) {

if(wordDict.size()==0) return false;

vector<bool> dp(s.size()+1,false);
dp[0]=true;
unordered_set<string> dict(wordDict.begin(), wordDict.end());

for(int i=1;i<=s.size();i++)
{
for(int j=i-1;j>=0;j--)
{
if(dp[j])
{
string word = s.substr(j,i-j);
if(dict.find(word)!= dict.end())
{
dp[i]=true;
break; //next i
}
}
}
}

return dp[s.size()];
}
};

Second time:

similar idea from previous one.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
vector<bool> dp(s.length()+1, false);
dp[0]= true;
unordered_set<string> dict;
for(auto n: wordDict)
dict.insert(n);

for(int i= 1; i<= s.length(); i++){
for(int j= i-1; j>= 0; j--){
if(dp[j]){
if(dict.find(s.substr(j, i-j)) != dict.end()){
dp[i]= true;
break;
}
}
}
}

return dp[s.length()];
}
};

Solution BFS

Modeled as a graph problem - every index is a vertex and every edge is a completed word
For example, the input string is “nightmare”, there are two ways to break it, “night mare” and “nightmare”. The graph would be

1
2
3
0-->5-->9

|__ __ _^

The question is simply to check if there is a path from 0 to 9. The most efficient way is traversing the graph using BFS with the help of a queue and a hash set. The hash set is used to keep track of the visited nodes to avoid repeating the same work.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
queue = deque()
visited = set()
queue.append(0)
visited.add(0)
while len(queue) > 0:
curr_index = queue.popleft()
for i in range(curr_index, len(s)+1):
if i in visited:
continue
if s[curr_index:i] in wordDict:
if i == len(s):
return True
queue.append(i)
visited.add(i)
return False