词典中最长的单词Java-前缀树
链接:词典中最长的单词
描述:给出一个字符串数组 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. 1 <= words.length <= 1000
2. 1 <= words[i].length <= 30
3. 所有输入的字符串 words[i] 都只包含小写字母。
分析:根据描述可知,最长的单词是由字典中较短的单词一步步叠加来的,也就是说字典中一定存在最长单词的所有前缀。
前缀树这种组合数据结构由此而生,当然,不同的题可以有不同的构造方法,基本思路如下:
1. 构造虚头节点
2. 构建头节点的孩子节点,由所有输入的字符串 words[i] 都只包含小写字母
可知,可以构造26个子节点,如:当传入的字符为a
时,在children[0]
处构造子节点,当传入字符为ab
时,在前一步基础上在children[0]
节点的children[1]
处构造子节点。
3. 当传入的字符构造结束时,在结束的子节点上将结束标志赋予true
如上图所示,当传入字典words = ["a", "d", "en"]
,前缀树构造如上,创造前缀树时从虚头节点开始,自顶向下创建,判断某个单词是否在前缀树中出现也从虚头节点开始,自顶向下判断单词中每个字符是否在节点中出现,最后判断isEnd
是否为True
回归本题,题意中返回 words 中最长的一个单词,该单词是由 words 词典中其他单词逐步添加一个字母组成。
,包含两层含义:
1.从虚头节点到最长单词,中间必有一条通路
2.通路每一层每个节点的isEND必须为True
故在前缀树中可实现两种方法,一是添加子节点,构造前缀树,二是判断某个单词是否由其他单词逐步添加一个字母而成
前缀树代码:
public class TrieTree {
private TrieTree[] children;
private boolean isEnd;
public TrieTree(){
this.children = new TrieTree[26];
this.isEnd = false;
}
public void insert(String word){
TrieTree temp = this; //得到虚头节点
for(int i=0;i<word.length();i++){
int pos = word.charAt(i)-'a';
if( temp.children[pos]==null) //如果此节点已存在,跳过构造
temp.children[pos] = new TrieTree(); //实例化子节点
temp = children[pos]; //指针移至子节点,继续构造后续节点
}
temp.isEnd = true; //单词末尾,将结束标志置为True
}
public boolean searchRes(String word){
TrieTree temp = this; //得到虚头节点
for(int i=0;i<word.length();i++){
int pos = word.charAt(i)-'a'; //依次判断单词中每个字符所处的节点位置
if(temp.children[pos]==null||!temp.children[pos].isEnd){ //当该位置上无结点时或该节点不是结束节点时,不满足上述两条含义所述的内容,说明该单词不是最长单词,直接pass掉
return false;
}
temp = temp.children[pos];
}
return true;
}
}
找出最长单词代码:
public static String longestWord(String[] words) {
String ans = "";
int len = 0;
TrieTree begin = new TrieTree();
for(String word:words){
begin.insert(word);
}
for(String word:words){
if(begin.searchRes(word)){ //当该单词满足上述两条含义基本内容时
int wordLen = word.length();
if(wordLen>len||(wordLen==len&&word.compareTo(ans)<0)){ // 判断该单词长度是否与目前最长单词长度长,如果True,将此单词记为最长单词,如果长度相同,将字典序小的记为最长单词
len = wordLen;
ans = word;
}
}
}
return ans;
}