这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战
链接
前置知识
- 字典树
- 字典树是以相同前缀为根节点设置的树,方便于检索,具体的实现见下面的代码。
- 在各种可以以相同前缀入手的问题中,字典树是一个可参考的方式,因为字符串长度不定,因此无论是用二进制来保存还是hash值,都不太好解决。
题解
分解为3个问题:
- 根据字典,构造字典树
- 根据字典树,构造dp数组,含义为:s为字符串,dp[i] [j] = (s[i] ~ s[j])是否在字典中能找到,如果有的话则为字典中的字符,否则则为-1
- 根据dp数组,求出整个字符串可由字典构成的所有可能
根据这个指导思想,三步走就得到如下代码:
static class Trie{
boolean root;
boolean word;
int wordIdx;
Trie[] leaves;
public Trie(boolean root) {
this.root = root;
leaves = new Trie[26];
wordIdx = -1;
}
public Trie gets(char c){
return leaves[c - 'a'];
}
public static Trie buildTrie(List<String> wordDict){
Trie root = new Trie(true);
for (int i1 = 0; i1 < wordDict.size(); i1++) {
String s = wordDict.get(i1);
Trie cur = root;
for (int i = 0; i < s.length(); i++) {
Trie level = cur.leaves[s.charAt(i) - 'a'];
if (level == null) {
level = new Trie(false);
cur.leaves[s.charAt(i) - 'a'] = level;
}
if (i == s.length() - 1) {
level.word = true;
level.wordIdx = i1;
}
cur = level;
}
}
return root;
}
}
public List<String> wordBreak(String s, List<String> wordDict) {
Trie root = Trie.buildTrie(wordDict);
List<String> res = new ArrayList<>();
int[][] dp = new int[s.length()][s.length()];
for (int i = 0; i < dp.length; i++) Arrays.fill(dp[i], -1);
updateDP(dp,s,0,root);
buildString(dp,res,wordDict,0,"");
return res;
}
public void updateDP(int[][] dp,String s,int start,Trie dic){
Trie cur = dic;
for(int j = start;j<s.length();j++){
Trie nxt = cur.gets(s.charAt(j));
if(nxt == null) break;
if(nxt.word){
dp[start][j] = nxt.wordIdx;
updateDP(dp,s,j+1,dic);
}
cur = nxt;
}
}
public void buildString(int[][] dp,List<String> res,List<String> dict,int curIdx,String startString){
if(curIdx==dp.length){
res.add(startString);
return;
}
if(curIdx!=0) startString += " ";
int[] ints = dp[curIdx];
for (int i1 = curIdx, intsLength = ints.length; i1 < intsLength; i1++) {
int dictIdx = dp[curIdx][i1];
if (dictIdx == -1) continue;
buildString(dp,res,dict,i1+1,startString+dict.get(dictIdx));
}
}
执行用时:1 ms, 在所有 Java 提交中击败了95.66%的用户
内存消耗:36.6 MB, 在所有 Java 提交中击败了63.25%的用户