package class48; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class Problem_0472_ConcatenatedWords { public static class TrieNode { public boolean end; public TrieNode[] nexts; public TrieNode() { end = false; nexts = new TrieNode[26]; } } public static void insert(TrieNode root, char[] s) { int path = 0; for (char c : s) { path = c - 'a'; if (root.nexts[path] == null) { root.nexts[path] = new TrieNode(); } root = root.nexts[path]; } root.end = true; } // 方法1:前缀树优化 public static List findAllConcatenatedWordsInADict1(String[] words) { List ans = new ArrayList<>(); if (words == null || words.length < 3) { return ans; } // 字符串数量 >= 3个 Arrays.sort(words, (str1, str2) -> str1.length() - str2.length()); TrieNode root = new TrieNode(); for (String str : words) { char[] s = str.toCharArray(); // "" 题目要求 if (s.length > 0 && split1(s, root, 0)) { ans.add(str); } else { insert(root, s); } } return ans; } // 字符串s[i....]能不能被分解? // 之前的元件,全在前缀树上,r就是前缀树头节点 public static boolean split1(char[] s, TrieNode r, int i) { boolean ans = false; if (i == s.length) { // 没字符了! ans = true; } else { // 还有字符 TrieNode c = r; // s[i.....] // s[i..end]作前缀,看看是不是一个元件!f(end+1)... for (int end = i; end < s.length; end++) { int path = s[end] - 'a'; if (c.nexts[path] == null) { break; } c = c.nexts[path]; if (c.end && split1(s, r, end + 1)) { ans = true; break; } } } return ans; } // 提前准备好动态规划表 public static int[] dp = new int[1000]; // 方法二:前缀树优化 + 动态规划优化 public static List findAllConcatenatedWordsInADict2(String[] words) { List ans = new ArrayList<>(); if (words == null || words.length < 3) { return ans; } Arrays.sort(words, (str1, str2) -> str1.length() - str2.length()); TrieNode root = new TrieNode(); for (String str : words) { char[] s = str.toCharArray(); Arrays.fill(dp, 0, s.length + 1, 0); if (s.length > 0 && split2(s, root, 0, dp)) { ans.add(str); } else { insert(root, s); } } return ans; } public static boolean split2(char[] s, TrieNode r, int i, int[] dp) { if (dp[i] != 0) { return dp[i] == 1; } boolean ans = false; if (i == s.length) { ans = true; } else { TrieNode c = r; for (int end = i; end < s.length; end++) { int path = s[end] - 'a'; if (c.nexts[path] == null) { break; } c = c.nexts[path]; if (c.end && split2(s, r, end + 1, dp)) { ans = true; break; } } } dp[i] = ans ? 1 : -1; return ans; } }