Given an array of strings words (without duplicates), return all the concatenated words in the given list ofwords.
A concatenated word is defined as a string that is comprised entirely of at least two shorter words in the given array.
Example 1:
**Input:** words = ["cat","cats","catsdogcats","dog","dogcatsdog","hippopotamuses","rat","ratcatdogcat"]
**Output:** ["catsdogcats","dogcatsdog","ratcatdogcat"]
**Explanation:** "catsdogcats" can be concatenated by "cats", "dog" and "cats";
"dogcatsdog" can be concatenated by "dog", "cats" and "dog";
"ratcatdogcat" can be concatenated by "rat", "cat", "dog" and "cat".
Example 2:
**Input:** words = ["cat","dog","catdog"]
**Output:** ["catdog"]
Constraints:
1 <= words.length <= 104
0 <= words[i].length <= 1000
words[i] consists of only lowercase English letters.
0 <= sum(words[i].length) <= 105
ac1: dp
similar to #139 work break. careful about "at least 2 words"
classSolution {publicList<String> findAllConcatenatedWordsInADict(String[] words) {List<String> res =newArrayList<>();// edge casesif (words ==null||words.length<2) return res;Set<String> set =newHashSet<>();for (String s : words) set.add(s);for (String s : words) {if (wordbreak(set, s)) res.add(s); }return res; }privatebooleanwordbreak(Set<String> set,String s) {boolean[] dp =newboolean[s.length()+1]; dp[0] =true;int cnt =0; // comprise of at least 2 wordsfor (int i =1; i <dp.length; i++) {for (int j = i -1; j >=0; j--) {if (dp[j] &&set.contains(s.substring(j, i))) { dp[i] =true; cnt++;if (j ==0&& i ==s.length()) cnt =1; // only single wordbreak; } } }return dp[dp.length-1] && cnt >1; }}
ac2: trie
classSolution {publicList<String> findAllConcatenatedWordsInADict(String[] words) {List<String> res =newArrayList<>();// edge casesif (words ==null||words.length<2) return res;TrieNode root =newTrieNode();for (String s : words) {if (s.length() ==0) continue; // edge caseadd(root, s); }for (String s : words) {if (validate(root, s,0)) res.add(s); }return res; }privatebooleanvalidate(TrieNode root,String s,int start) {TrieNode r = root;for (int i = start; i <s.length(); i++) {int pos =s.charAt(i) -'a';if (r.next[pos] ==null) returnfalse; r =r.next[pos];if (r.isWord) {if (validate(root, s, i+1)) returntrue; } }returnr.isWord&& start !=0; }privatevoidadd(TrieNode root,String s) {TrieNode r = root;for (int i =0; i <s.length(); i++) {int pos =s.charAt(i) -'a';if (r.next[pos] ==null) r.next[pos] =newTrieNode(); r =r.next[pos]; }r.isWord=true; }}classTrieNode {TrieNode[] next =newTrieNode[26];boolean isWord =false;}/*Time complexity:1) Build trie: O(nk), n for words.size(), k for avg length of words.2) Validate: O(nk).Space: Trie worst case n*k, where no word share same branch in trie.*/