139. Word Break (Medium)
Given a non-empty stringsand 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 1: DP O(m * n^2); O(n) m is the size of dict
public boolean wordBreak(String s, List<String> wordDict) {
if (s == null || s.length() == 0) {
return true;
}
int n = s.length();
boolean[] dp = new boolean[n + 1];
dp[0] = true;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < i; j++) {
// for (int j = i - 1; j >= 0; j--) { //a small optimization
if (dp[j] && wordDict.contains(s.substring(j, i))) {
dp[i] = true;
break;
}
}
}
return dp[n];
}
Solution 2: Trie + DP
public class TrieNode {
boolean isWord;
TrieNode[] c;
public TrieNode() {
isWord = false;
c = new TrieNode[128];
}
}
public void addWord(TrieNode t, String w) {
for (int i = 0; i < w.length(); i++) {
int j = (int) w.charAt(i);
if (t.c[j] == null) t.c[j] = new TrieNode();
t = t.c[j];
}
t.isWord = true;
}
public boolean wordBreakB(String s, List<String> wordDict) {
TrieNode t = new TrieNode(), cur;
for (String i : wordDict) addWord(t, i);
char[] str = s.toCharArray();
int len = str.length;
boolean[] f = new boolean[len + 1];
f[len] = true;
for (int i = len - 1; i >= 0; i--) {
//System.out.println(str[i]);
cur = t;
for (int j = i; cur != null && j < len; j++) {
cur = cur.c[(int) str[j]];
if (cur != null && cur.isWord && f[j + 1]) {
f[i] = true;
break;
}
}
}
return f[0];
}
Follow Up:
- 输出一组解
可以直接将boolean改成int,不需要第二个数组,见第二种解法
public String wordBreakC(String s, List<String> wordDict) {
if (s == null || s.length() == 0) {
return "";
}
int n = s.length();
int[] end = new int[n + 1]; //数组的索引记录最长分段的开始位置,数组的值记录最长段的结束位置
// end[0] = -1;//没有必要赋值为-1,因为如果有解,end[0]的值肯定会改变
boolean[] dp = new boolean[n + 1];
dp[0] = true;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < i; j++) {
if (dp[j] && wordDict.contains(s.substring(j, i))) {
dp[i] = true;
end[j] = i; //这样导致输出的解的第一段是dict中s的最长prefix
break;
}
}
}
if (!dp[n]) {
return "";
}
StringBuilder sb = new StringBuilder(s.substring(0, end[0]));
int start = end[0];
while (start < s.length()) {
sb.append(" " + s.substring(start, end[start])); //StringBuilder append只能在尾部加字符串
start = end[start];
}
return sb.toString();
}
public String wordBreakD(String s, List<String> wordDict) { //上面解法的简化版
if (s == null || s.length() == 0) {
return "";
}
int n = s.length();
int[] dp = new int[n + 1];
Arrays.fill(dp, -1);
dp[0] = 0;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < i; j++) {
if (dp[j] != -1 && wordDict.contains(s.substring(j, i))) {
dp[i] = j;
// System.out.println(i + " ");
break;
}
}
}
if (dp[n] == -1) {
return "";
}
StringBuilder sb = new StringBuilder(s.substring(dp[n], n));
int end = dp[n];
while (end > 0) {
sb.insert(0, s.substring(dp[end], end) + " "); //insert可以在头部加字符串,但是开销是O(n)
end = dp[end];
}
return sb.toString();
}