【LeetCode】720. Longest Word in Dictionary(C++)

地址:https://leetcode.com/problems/longest-word-in-dictionary/

题目:

Given a list of strings words representing an English Dictionary, find the longest word in words that can be built one character at a time by other words in words. If there is more than one possible answer, return the longest word with the smallest lexicographical order.

Example 1:

Input: words = [“w”,“wo”,“wor”,“worl”, “world”]
Output: “world”
Explanation: The word “world” can be built one character at a time by “w”, “wo”, “wor”, and “worl”.

Example 2:

Input: words = [“a”, “banana”, “app”, “appl”, “ap”, “apply”, “apple”]
Output: “apple”
Explanation: Both “apply” and “apple” can be built from other words in the dictionary. However, “apple” is lexicographically smaller than “apply”.

Note:

All the strings in the input will only contain lowercase letters.

  • The length of words will be in the range [1, 1000].
  • The length of words[i] will be in the range [1, 30].

理解:

承认愚蠢的我并没有看懂题目。。遂google之
是这样的,找到words中最长的那个单词,他可以通过其他的words每次加一个字母得到。比如Example 1中,world可以依次由"w",“wo”,“wor”,"worl"添加’o’,‘r’,‘l’,'d’得到。如果有多个可能的答案,返回按字典序最小的,也就是最后一个字母最小的,比如apple和apply,字典序apple在前,返回apple。

实现1:

看到discussion里的Trie+DFS,感觉略麻烦,要去吃饭了,先看一种简单的实现。
首先把words按字典序排序,从而保证字典序小的word在前,然后遍历。创建一个unordered_set

  • 如果长度为1,就加入s,因为可能是其他单词的前缀,并更新res
  • 如果这个单词word的前word.size()-1项在set中,表明word可以由words中的单词添加一个字母构成,就更新res,并加入s
  • 注意更新res的时候,只有长度大于现有的res才更新,这样保证了,res为同长度下字典序最小的字符串。

实现如下

class Solution {
public:
	string longestWord(vector<string>& words) {
		string res;
		unordered_set<string> s;
		sort(words.begin(), words.end());
		for (string word : words) {
			if (word.size() == 1 || s.count(word.substr(0, word.size() - 1))) {
				res = word.size() > res.size() ? word : res;
				s.insert(word);
			}
		}
        return res;
	}
};

先去吃饭了,下午再补复杂的方法。

实现2

先补一些基础知识
trie:又称前缀树或字典树,是一种有序树,用于保存关联数组,其中的键通常是字符串。与二叉查找树不同,键不是直接保存在节点中,而是由节点在树中的位置决定。一个节点的所有子孙都有相同的前缀,也就是这个节点对应的字符串,而根节点对应空字符串。一般情况下,不是所有的节点都有对应的值,只有叶子节点和部分内部节点所对应的键才有相关的值。Trie-维基百科
基本性质:

  • 根节点不包含字符,除根节点外的每一个子节点都包含一个字符。
  • 从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串。
  • 每个节点的所有子节点包含的字符互不相同。
    一棵Trie树,表示了关键字集合{“a”, “to”, “tea”, “ted”, “ten”, “i”, “in”, “inn”} 。
    上面的Trie树,表示了关键字集合{“a”, “to”, “tea”, “ted”, “ten”, “i”, “in”, “inn”} 。来源:Trie树(Prefix Tree)介绍
    因此,插入所有单词,构成一个Tier即可。实现如下:
//Definition of TrieNode
class TrieNode {
public:
	bool last;
	vector<TrieNode*> children;
	TrieNode():last(false),children(26,nullptr){}
};

class Trie {
public:
	TrieNode* root;
	Trie():root(new TrieNode()){}
	void insert(string s) {
		TrieNode* curr = root;
		for (char c : s) {
			int index = c - 'a';
			if (!curr->children[index]) {
				curr->children[index] = new TrieNode();
			}
			curr = curr->children[index];
		}
		curr->last = true;
	}
	void dfs(TrieNode* curr, string& res, string& s) {
		if (curr->last) {
			if (s.size() > res.size())
				res = s;
		}
		for (int index = 0; index < 26;++index) {
			if (curr->children[index]&& curr->children[index]->last) {
				s.push_back(index + 'a');
				dfs(curr->children[index], res, s);
				s.pop_back();
			}
		}
	}

	string dfsSearch() {
		string res, s;
		dfs(root, res, s);
		return res;
	}
};

class Solution {
public:
	string longestWord(vector<string>& words) {
		Trie t;
		for (string word : words)
			t.insert(word);
		return t.dfsSearch();
	}
};

实现3

提交完了以后,还看到一种简单粗暴的方法。
对words建立一个unordered_set,然后遍历words,对每个word,

  • 若比res短,或者相同,但字典序大于等于res时,跳过
  • 否则,循环去判断,每去掉一个后缀,剩余的是不是在哈希表里,如果有不在的就不满足,如果都在,则标记为新的res
class Solution {
public:
	string longestWord(vector<string>& words) {
		unordered_set<string> s(words.size());
		for (auto &w : words) s.insert(w);

		string rs;
		for (auto &w : words) {
			if (w.size() < rs.size() || (w.size() == rs.size() && w >= rs)) continue;

			bool found = true;
			string temp = w;
			while (temp.size() > 1) {
				temp.pop_back();
				if (!s.count(temp)) { found = false; break; }
			}

			if (found) rs = w;
		}
		return rs;
	}
};
<think>嗯,用户想了解如何用哈希表解决问题。首先,我得回忆哈希表的基本概念。哈希表是一种数据结构,通过哈希函数将键映射到存储位置,从而实现快速的数据访问。常见的应用包括快速查找、去重、统计频率等。 接下来,我需要考虑用户可能的背景。可能是刚开始学习数据结构,或者遇到具体问题需要哈希表的解决方案。需要从基础讲起,但也要给出实际例子,让用户明白应用场景。 要分步骤解释哈希表的工作原理。比如哈希函数的作用,处理冲突的方法,如开放寻址法和链地址法。然后,举几个典型例子,比如两数之和、统计字符出现次数,这样用户能直观理解。 还要注意用户可能混淆哈希表和其他数据结构,比如数组或二叉搜索树,所以需要比较不同方法的优缺点,突出哈希表的效率优势,比如O(1)的平均时间复杂度。 另外,用户可能想知道在实际编程中如何实现,比如在Python中使用字典,Java中用HashMap,JavaScript用对象或Map。这部分需要简要提及,增强实用性。 最后,总结哈希表的适用场景,比如需要快速查找、插入、删除的情况,以及空间换时间的权衡。确保回答结构清晰,由浅入深,帮助用户建立全面的理解。</think>哈希表(Hash Table)是一种通过**键值对**存储数据的高效数据结构,其核心思想是通过哈希函数将键(Key)转换为索引,直接访问对应存储位置。以下是其工作原理和典型应用场景的分步说明: --- ### **一、哈希表的核心组成** 1. **哈希函数** - 将任意大小的键(如字符串、对象)映射为固定范围的整数(即哈希值)。 - 示例:`hash(key) = key % 10`(取余操作) 2. **存储结构** - 通常是一个数组,每个位置称为"桶"(Bucket)。 - 哈希值决定数据存储在哪个桶中。 3. **冲突处理** - 不同键可能生成相同的哈希值(哈希冲突),常用解决方法: - **链地址法**:每个桶存储链表,冲突元素追加到链表尾部(如Java的`HashMap`)。 - **开放寻址法**:冲突时按规则探测下一个空桶(如线性探测、平方探测)。 --- ### **二、哈希表的典型应用** #### 1. 快速查找(O(1)平均时间复杂度) **示例:实现字典** - 存储单词和释义,输入单词直接查释义。 - Python代码片段: ```python dictionary = {"apple": "苹果", "banana": "香蕉"} print(dictionary["apple"]) # 输出“苹果” ``` #### 2. 数据去重 **示例:统计唯一元素** - 遍历数组,用哈希表记录已出现元素。 - 代码逻辑: ```python def find_unique(nums): seen = {} for num in nums: if num not in seen: seen[num] = True return list(seen.keys()) ``` #### 3. 频率统计 **示例:统计字符出现次数** - 遍历字符串,用哈希表记录每个字符的频率。 ```python from collections import defaultdict def count_chars(s): counter = defaultdict(int) for c in s: counter[c] += 1 return counter ``` #### 4. 缓存优化(如LRU Cache) - 通过哈希表+双向链表实现快速访问和淘汰机制。 --- ### **三、经典算法问题** #### 1. 两数之和(LeetCode 1) **问题**:在数组中找到两数,使它们的和等于目标值。 **哈希解法**: - 遍历数组,用哈希表记录`{数值: 索引}`。 - 对每个元素`num`,检查`target - num`是否已存在表中。 - 时间复杂度:O(n)。 ```python def two_sum(nums, target): hashmap = {} for i, num in enumerate(nums): complement = target - num if complement in hashmap: return [hashmap[complement], i] hashmap[num] = i return [] ``` #### 2. 最长无重复子串(LeetCode 3) **问题**:找字符串中不重复字符的最长子串长度。 **优化解法**: - 用哈希表记录字符最后一次出现的位置。 - 滑动窗口动态更新起始位置。 ```python def length_of_longest_substring(s): char_map = {} max_len = start = 0 for end, c in enumerate(s): if c in char_map and char_map[c] >= start: start = char_map[c] + 1 # 移动窗口左边界 char_map[c] = end max_len = max(max_len, end - start + 1) return max_len ``` --- ### **四、哈希表的优缺点** | 优势 | 局限性 | |------|--------| | 平均O(1)的插入、删除、查找 | 最坏情况时间复杂度退化为O(n)(如所有键冲突)| | 灵活支持多种数据类型 | 需要额外内存空间存储哈希表结构 | | 天然适合键值对场景 | 无序(有序哈希表需特殊实现)| --- ### **五、实际开发中的选择** - **编程语言支持**: Python用`dict`,Java用`HashMap`,C++用`unordered_map`。 - **哈希函数设计**:需尽量减少冲突,均匀分布键。 - **动态扩容**:当元素数量超过负载因子阈值时,自动扩容并重新哈希。 通过合理使用哈希表,可以显著优化算法效率,尤其适合处理需要快速查找和关联关系的场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值