给出一个字符串数组 words 组成的一本英语词典。返回 words 中最长的一个单词,该单词是由 words 词典中其他单词逐步添加一个字母组成。若其中有多个可行的答案,则返回答案中字典序最小的单词。若无答案,则返回空字符串。
示例 1:
输入:words = [“w”,“wo”,“wor”,“worl”, “world”]
输出:“world”
解释: 单词"world"可由"w", “wo”, “wor”, 和 "worl"逐步添加一个字母组成。
示例 2:
输入:words = [“a”, “banana”, “app”, “appl”, “ap”, “apply”, “apple”]
输出:“apple”
解释:“apply” 和 “apple” 都能由词典中的单词组成。但是 “apple” 的字典序小于 “apply”
提示:
1 <= words.length <= 1000
1 <= words[i].length <= 30
所有输入的字符串 words[i] 都只包含小写字母。
第一种思路:先将所有的 words[i] 存入 Set 集合,后面可以查询某个子串是否存在 words 中。遍历 words 数组,判断每个 words[i] 是否为合法单词,同时利用当前的最长单词来做剪枝。
class Solution {
public String longestWord(String[] words) {
String res = "";
Set<String> dic = new HashSet<>();
for(String i : words){
dic.add(i);
}
for(String i : dic){
int m = res.length(), n = i.length();
if(n < m){
continue;
}
if(m == n && i.compareTo(res) > 0){
continue;
}
boolean flag = true;
for(int j = 1; j <= n && flag; j++){
String sub = i.substring(0,j);
if(!dic.contains(sub)){
flag = false;
}
}
if(flag){
res = i;
}
}
return res;
}
}
第二种思路:建立前缀树,前缀树是一颗多叉树,一个节点可能有多个子节点,前缀树除根节点外,每个节点表示字符串中的一个字符,而字符串由前缀树的路径表示。
class Solution {
class TrieNode{
TrieNode[] children;
//标志是否是一个完整的字符串
boolean isWord;
public TrieNode(){
children = new TrieNode[26];
}
}
TrieNode root = new TrieNode();
public void insert(String word) {
TrieNode node = root;
for(char ch : word.toCharArray()){
if(node.children[ch - 'a'] == null){
node.children[ch - 'a'] = new TrieNode();
}
node = node.children[ch-'a'];
}
node.isWord = true;
}
public boolean search(String word) {
TrieNode node = root;
for(char ch : word.toCharArray()){
if(node.children[ch - 'a'] == null){
return false;
}
node = node.children[ch-'a'];
//如果不是每次连续加一个字符,则不满足题意
if(node.isWord == false){
return false;
}
}
return true;
}
public String longestWord(String[] words) {
String res = "";
for(String s : words){
insert(s);
}
for(String s : words){
int m = res.length(), n = s.length();
if(n < m){
continue;
}
if(m == n && s.compareTo(res) > 0){
continue;
}
if(search(s)){
res = s;
}
}
return res;
}
}