原题网址:https://leetcode.com/problems/word-break-ii/
Given a string s and a dictionary of words dict, add spaces in s to construct a sentence where each word is a valid dictionary word.
Return all such possible sentences.
For example, given
s = "catsanddog"
,
dict = ["cat", "cats", "and", "sand", "dog"]
.
A solution is ["cats and dog", "cat sand dog"]
.
方法一:深度优先。
public class Solution {
private ArrayList<String>[] scanned;
private List<String> results = new ArrayList<>();
private void dfs(String s, int from, List<String> words, Set<String> wordDict) {
if (from == s.length()) {
String result = "";
for(int i=0; i<words.size(); i++) {
if (i>0) result += " ";
result += words.get(i);
}
results.add(result);
return;
}
for(String word: scanned[from]) {
words.add(word);
dfs(s, from + word.length(), words, wordDict);
words.remove(words.size()-1);
}
}
public List<String> wordBreak(String s, Set<String> wordDict) {
scanned = new ArrayList[s.length()];
scanned[0] = new ArrayList<>();
boolean reachable = false;
for(int i=0; i<s.length(); i++) {
if (scanned[i] == null) continue;
for(String word: wordDict) {
if (i + word.length() <= s.length() && word.equals(s.substring(i, i+word.length()))) {
scanned[i].add(word);
if (i+word.length() == s.length()) reachable = true;
if (i+word.length() < s.length() && scanned[i+word.length()] == null) {
scanned[i+word.length()] = new ArrayList<>();
}
}
}
}
if (!reachable) return results;
dfs(s, 0, new ArrayList<>(), wordDict);
return results;
}
}
优化以避免重复的判断:
public class Solution {
private void find(String s, List<Integer>[] substrings, int from, List<String> currents, List<String> results) {
// System.out.printf("from=%d, currents=%s\n", from, currents);
if (from == s.length()) {
StringBuilder sb = new StringBuilder();
for(String current : currents) {
if (sb.length() > 0) sb.append(" ");
sb.append(current);
}
results.add(sb.toString());
return;
}
for(int length : substrings[from]) {
currents.add(s.substring(from, from + length));
find(s, substrings, from + length, currents, results);
currents.remove(currents.size() - 1);
}
}
public List<String> wordBreak(String s, Set<String> wordDict) {
Set<Integer> lengthSet = new HashSet<>();
for(String word : wordDict) {
lengthSet.add(word.length());
}
// System.out.printf("lengthSet=%s\n", lengthSet);
int[] lengths = new int[lengthSet.size()];
int l = 0;
for(int length : lengthSet) {
lengths[l++] = length;
}
List<Integer>[] substrings = new List[s.length() + 1];
substrings[0] = new ArrayList<>();
for(int i = 0; i < s.length(); i++) {
if (substrings[i] == null) continue;
for(int j = 0; j < lengths.length; j++) {
if (i + lengths[j] <= s.length() && wordDict.contains(s.substring(i, i + lengths[j]))) {
substrings[i].add(lengths[j]);
if (substrings[i + lengths[j]] == null) {
substrings[i + lengths[j]] = new ArrayList<>();
}
}
}
}
// for(int i = 0; i < substrings.length; i++) {
// System.out.printf("substrings[%d]=%s\n", i, substrings[i]);
// }
List<String> results = new ArrayList<>();
if (substrings[s.length()] == null) return results;
find(s, substrings, 0, new ArrayList<>(), results);
return results;
}
}
方法二:Trie+深度优先搜索。
public class Solution {
private TrieNode root;
private void find(char[] sa, int from, int[] split, int splits, List<String> words) {
if (from == sa.length) {
char[] word = new char[sa.length+splits-1];
int wordPos = 0, splitPos = 0;
for(int i=0; i<sa.length; i++) {
if (i==split[splitPos]) { word[wordPos++] = ' '; splitPos++; }
word[wordPos++] = sa[i];
}
words.add(new String(word));
return;
}
TrieNode current = root;
for(int i=from;i<sa.length;i++) {
current = current.nexts[sa[i]-'a'];
if (current == null) break;
if (current.isWord) {
split[splits] = i+1;
find(sa, i+1, split, splits+1, words);
}
}
}
public List<String> wordBreak(String s, Set<String> wordDict) {
List<String> results = new ArrayList<>();
if (s==null || wordDict==null) return results;
root = new TrieNode();
for(String word: wordDict) {
char[] wa = word.toCharArray();
TrieNode current = root;
for(int i=0; i<wa.length; i++) current = current.add(wa[i]);
current.isWord = true;
}
char[] sa = s.toCharArray();
boolean[] reachable = new boolean[sa.length+1];
reachable[0] = true;
for(int i=0; i<sa.length; i++) {
if (!reachable[i]) continue;
TrieNode current = root;
for(int j=i;j<sa.length && current != null;j++) {
current = current.nexts[sa[j]-'a'];
if (current != null && current.isWord) reachable[j+1] = true;
}
}
if (!reachable[sa.length]) return results;
find(sa, 0, new int[sa.length], 0, results);
return results;
}
}
class TrieNode {
boolean isWord;
TrieNode[] nexts = new TrieNode[26];
TrieNode add(char ch) {
int i = ch - 'a';
if (nexts[i] != null) return nexts[i];
nexts[i] = new TrieNode();
return nexts[i];
}
}