https://leetcode.cn/problems/word-break/description/?envType=study-plan-v2&envId=top-interview-150

class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
// dp[i]表示[0, i)字符串可否构建
// 那么dp[i]可构建的条件是,[0,j)可构建且[j,i)包含在wordDict中
boolean[] dp = new boolean[s.length() + 1];
dp[0] = true;
Set<String> set = new HashSet<>(wordDict);
for (int i = 1; i <= s.length(); i++) {
for (int j = 0; j < i; j++) {
if (dp[j] == true && set.contains(s.substring(j, i))) {
dp[i] = true;
break;
}
}
}
return dp[s.length()];
}
}
O(c*s.length)

O( s.length)
class Solution {
Trie root = new Trie();
// 记忆数组,用来快速判定该位置是否可以作为单词结尾进行拆分构建
boolean[] no = new boolean[300];
public boolean wordBreak(String s, List<String> wordDict) {
// 将所有word插入字典树
for (String word : wordDict)
root.insert(word);
// 从0个字符开始往后查找,只要匹配成功说明可以构建目标字符串
if (root.find(s, 0)) {
return true;
}
return false;
}
class Trie{
public Trie[] children = new Trie[26];
// 当前child代表的字符是否是单词结尾
boolean isEnd = false;
public void insert(String word) {
if (null == word || word.length() == 0) {
isEnd = true;
return;
}
int index = word.charAt(0) - 'a';
Trie child = children[index];
if (null == child) {
child = new Trie();
children[index] = child;
}
child.insert(word.substring(1));
}
public boolean find(String s, int i) {
// 快速判定当前字符位置是否可以拆分构建
// 注意这里必须判定当前节点是否是root,因为我们缓存是从根节点开始的
// 否则会对其他child的正常判断过程造成误判
if (this == root && no[i]) {
return false;
}
char firstC = s.charAt(i);
Trie child = children[firstC - 'a'];
if (null == child) {
// 如果不能匹配指定位置字符,肯定不可构建
if (this == root) {
no[i] = true;
}
return false;
}
if (child.isEnd) {
// 如果能找到目标字符,且字符是单词结尾
if (i + 1 == s.length()) {
// 如果
// 1.已经扫描到字符串最后的字符
// 就说明当前位置可以用来拆分构建目标字符串
return true;
} else {
if (root.find(s, i+1)) {
// 如果下一个字符往后的字符串能构建
// 就说明当前位置可以用来拆分构建目标字符串
return true;
} else {
// 否则说明i+1字符虽是单词结尾,但无法直接拆分构建,记录下来
no[i+1] = true;
}
}
}
if (i + 1 < s.length()) {
// 还未到结尾,可以继续往后查找
return child.find(s, i+1);
} else {
// 已到单词结尾,构建失败
return false;
}
}
}
}

O(s.length)