720 词典中最长的单词(Trie树)

一时失言乱红尘 2022-09-09 12:51 293阅读 0赞

1. 问题描述:

给出一个字符串数组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”。

提示:

所有输入的字符串都只包含小写字母。
words数组长度范围为[1,1000]。
words[i]的长度范围为[1,30]。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-word-in-dictionary

2. 思路分析:

分析题目可以知道我们需要求解出所有单词最长的前缀,根据这个特点可以知道我们可以使用Trie树来解决,Trie树适合求解字符串的插入与前缀查询操作。因为使用的是python语言所以我们可以声明一个二维列表son(c++/java等语言可以声明二维数组),其中第一维的长度为N,N为Trie树中节点的个数,由题目可知words长度为1000,每个单词长度为30所以Trie树中节点个数最多为1000 * 30 = 30000,所以第一维声明比30000长一点即可(可以将每一个字符看成是Trie树中的一个节点),第一维表示当前字符串的前缀对应的编号,第二维表示当前字符串结尾的字符对应的位置,长度为26表示26个字母对应的位置,我们需要借助于一个int类型的变量idx来唯一标识Trie树中的每一个节点,这样第一维的编号p加上当前编号对应单词的结尾字符就可以判断出对应前缀是否存在。并且我们需要声明一个一维列表is_end来记录插入节点的时候对应单词的编号,这样我们在后面找对应答案的时候可以根据当前的节点编号p找到对应的单词编号;查询最长的前缀的时候我们可以使用dfs搜索整棵Trie树(并且Trie树中先搜索到的节点一定是字典序最小的节点),从Trie树中的第一个节点出发,搜索Trie树中存在的节点,并且对应的节点编号在is_end中存在则往下递归搜索,is_end中对应的编号p存在说明words中是存在当前节点编号对应的单词的,也即搜索有结束标记的节点才是有效的,我们可以从上往下搜索Trie树,并且递归返回到上一层调用位置的时候通过递归的结果来更新上一层节点对应的长度以及对应的单词位置,由下往上更新这样最终到Trie树根节点的时候就已经求解出最长的前缀以及在words中对应的编号,返回这个编号即可。

watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBAeXV6aGFuZ196eQ_size_16_color_FFFFFF_t_70_g_se_x_16

3. 代码如下:

  1. from typing import List
  2. class Solution:
  3. son = None
  4. N = 30010
  5. # idx用来唯一标识Trie树中的每一个节点
  6. idx = 0
  7. # 用来记录后面的答案, 表示的是哪一个单词, is_end[p]表示p这个节点对应的单词编号
  8. is_end = None
  9. # Trie树中插入节点的操作, k用来标记插入的是哪一个单词
  10. def insert(self, w: str, k: int):
  11. son = self.son
  12. is_end = self.is_end
  13. p = 0
  14. for c in w:
  15. t = ord(c) - ord("a")
  16. if son[p][t] == 0:
  17. son[p][t] = self.idx
  18. self.idx += 1
  19. p = son[p][t]
  20. # 当前p这个点对应当前单词的下标
  21. is_end[p] = k
  22. # dfs搜索Trie树, 走有对应单词标记的节点
  23. def dfs(self, l: int, p: int):
  24. res = [l, self.is_end[p]]
  25. for i in range(26):
  26. j = self.son[p][i]
  27. if j > 0 and self.is_end[j] != -1:
  28. t = self.dfs(l + 1, self.son[p][i])
  29. # 往下走发现答案更大那么更新答案第一个找到的答案肯定是字典序最小的
  30. if t[0] > res[0]: res = t
  31. return res
  32. def longestWord(self, words: List[str]) -> str:
  33. # 初始化Trie树的所有节点
  34. self.son = [[0] * 26 for i in range(self.N)]
  35. self.idx = 1
  36. # 用来记录每一个节点对应单词的下标
  37. self.is_end = [-1] * self.N
  38. for i in range(len(words)):
  39. self.insert(words[i], i)
  40. # 搜索整棵树
  41. t = self.dfs(0, 0)
  42. if t[1] != -1: return words[t[1]]
  43. return ""

发表评论

表情:
评论列表 (有 0 条评论,293人围观)

还没有评论,来说两句吧...

相关阅读

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

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

    相关 720. 词典单词

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

    相关 Java 单词

    编写一个函数,输入一行字符,将此字符串中最长的单词输出。   输入仅一行,多个单词,每个单词间用一个空格隔开。单词仅由小写字母组成。所有单词的长度和不超过100000。如有