【LeetCode】第720题——词典中最长的单词(难度:简单)

【LeetCode】第720题——词典中最长的单词(难度:简单)

  • 题目描述
  • 解题思路
  • 代码详解
  • 注意点

题目描述

给出一个字符串数组words组成的一本英语词典。从中找出最长的一个单词,该单词是由words词典中其他单词逐步添加一个字母组成。若其中有多个可行的答案,则返回答案中字典序最小的单词。

若无答案,则返回空字符串。

  1. 示例 1:
    输入:
    words = [“w”,“wo”,“wor”,“worl”, “world”]
    输出:“world”
    解释:
    单词"world"可由"w", “wo”, “wor”, 和 "worl"添加一个字母组成。

  2. 示例 2:
    输入:
    words = [“a”, “banana”, “app”, “appl”, “ap”, “apply”, “apple”]
    输出:“apple”
    解释:
    “apply"和"apple"都能由词典中的单词组成。但是"apple"的字典序小于"apply”。

提示:
所有输入的字符串都只包含小写字母。
words数组长度范围为[1,1000]。
words[i]的长度范围为[1,30]。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-word-in-dictionary
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

现在引入一个新的名词——单词查找树(Trie),其根节点的val一般不放值或者放入一个无关的值,其后续所有孩子节点的val都只是一个字符,从根节点到叶子节点的顺序可以将遍历到的字符组合成一个字符串,就像英文输入法中输入单词的前几个字母就可以提示出以这几个字母为开头的英文单词的集合。

然后,以示例2为例,按照Trie可以构建apple、apply、banana三棵子树。这时需要引入一个判断量A(A是判断量的初始值),在构建Trie时只在最后一个节点赋某一不同于初始判断量的值B(各个B的值可以不一样)即可。如此一来,在遍历树时apple、apply这两棵树上的所有节点的判断量都不是A,而banana树上的第一个有效节点字符“b”的判断量是A,证明所给的字符串集合内没有其前置单词,banana在此时便失去了作为结果的价值。这时候只需对apple和apply字符串使用.length()和.compareTo()方法即可得出答案。

代码详解

class Solution {
    public String longestWord(String[] words) {
        Tire tire = new Tire();	// 构建Tire()对象,此对象的类需要自己编写
        tire.words = words;		// 为成员变量赋值
        int index = 0;			// wordsd的索引下标,即可作为上一小节提到的判断量B,又可方便索引
        for(String s : words) {
            tire.insert(s, index);	// 逐个单词地构建树
            ++index;
        }
        return tire.dfs();		// BFS遍历构建的树
    }
}

class Tire {

    String[] words;			// 就是题目所给的words
    Node root = new Node();	// 构建根节点

    void insert(String s, int index) {
        Node cur = root;	// cur是一个动态节点
        char[] chars = s.toCharArray();	// 将传入的字符串分割成一个个字符
        for(char c : chars) {
            cur.children.putIfAbsent(c, new Node());	// 不存在该字符就创建其孩子节点
            cur = cur.children.get(c);	// 指针指向其孩子节点,继续建树
        }
        cur.index = index;	// 判断量,赋值一个不同于初始判断量的值即可,使用索引值还可方便后续索引,一举两得
    }

    String dfs() {
        String ans = ""; // 空字符串长度最小,且当未取得期望结果时可返回空字符串,符合题意
        Stack<Node> stack = new Stack<>(); // 通过栈+循环实现dfs,当然也可用递归,也可用队列+循环实现bfs遍历
        stack.push(root);
        while(!stack.isEmpty()) {
            Node n = stack.pop();
            // 判断条件1是否为根节点
            // 如果不是则观察“判断量”是否为默认值-1
            // 不是-1就说明从根节点到当前节点所形成的字符串在字典中出现过
            if(n != root && n.index != -1) {
                String s = words[n.index]; // 索引值在此起作用
                // compareTo()为了获取字典序更小的单词(单词长度相等时)
                if(s.length() > ans.length() || (s.length() == ans.length() && s.compareTo(ans) < 0)) {	
                    ans = s;
                }
            // 是默认值-1的话说明该单词的前置未出现过,不遍历其孩子节点了
            } else if(n != root && n.index == -1) {
                continue;
            }
            // 如果是根节点则把孩子节点压入栈
            for(Node child : n.children.values()) {
                stack.push(child);
            }          
        }
        return ans;
    }
}

class Node {
    int index = -1; // 判断量的初始值为-1
    HashMap<Character, Node> children = new HashMap<>();
}

注意点

  • 几乎全是新的知识点,话说这真是简单题嘛555

你可能感兴趣的:(LeetCode题解,leetcode,trie,java,dfs,前缀)