往期文章 :
- LeetCode 剑指 Offer II 链表 专题总结
- LeetCode 剑指 Offer II 哈希表 专题总结
- LeetCode 剑指 Offer II 栈 专题总结
- LeetCode 剑指 Offer II 队列 专题总结
- LeetCode 剑指 Offer II 树(上) 专题总结
- LeetCode 剑指 Offer II 树(下) 专题总结
- LeetCode 剑指 Offer II 堆 专题总结
- LeetCode 剑指 Offer II 前缀树(上) 专题总结
第一题:后缀,
第二题:前缀,用哈希更好
第三题:01 Trie树
目录
065. 最短的单词编码
题目:
示例:
提示:
1 <= words.length <= 2000
1 <= words[i].length <= 7
words[i]
仅由小写字母组成
思路:
class Trie {
public:
vector<Trie*> next;
bool isEnd;
Trie():next(26, nullptr),isEnd(false){}
void insert(string& word) {
Trie* node = this;
// 反向插入
for(int i = word.length() - 1; i >= 0; i--) {
int index = word[i] - 'a';
if(node->next[index] == nullptr) {
node->next[index] = new Trie();
}
node = node->next[index];
}
node->isEnd = true;
}
int searchLen(string& word) {
Trie* node = this;
for(int i = word.length() - 1; i >= 0; i--) {
int index = word[i] - 'a';
node = node->next[index];
// 找到单词最后一个字符,并且该字符是单词结尾
if(node->isEnd && i == 0) {
// 判断这个字符是不是结尾,不是的话就是后缀,不用处理
for(int i = 0; i < 26; i++) {
if(node->next[i]) {
return 0;
}
}
// 将这个单词删除,将相同元素 adn 后缀元素删除
node->isEnd = false;
// 这个字符是结尾,返回 单词 + #
return word.length() + 1;
}
}
return 0;
}
};
class Solution {
public:
// 思路:本题含义是如果 x 是 y 的后缀,就删除x,不用统计
int minimumLengthEncoding(vector<string>& words) {
Trie* node = new Trie();
for(auto& word : words) {
node->insert(word);
}
int ans = 0;
for(auto& word : words) {
ans += node->searchLen(word);
}
return ans;
}
};
066. 单词之和
题目:
示例:
提示:
1 <= key.length, prefix.length <= 50
key
和prefix
仅由小写英文字母组成1 <= val <= 1000
- 最多调用
50
次insert
和sum
思路:
这道题用哈希表反而比前缀树简单不止一点半点,而且还快
题意:难点是 sum()
函数,要找到prefix
前缀的单词的val
和
方法一:哈希表
class MapSum {
public:
unordered_map<string, int> map;
MapSum() {
}
void insert(string key, int val) {
map[key] = val;
}
int sum(string prefix) {
int ans = 0, n = prefix.length();
for(auto& [key, val] : map) {
if(key.length() >= n && key.substr(0, n) == prefix)
ans += val;
}
return ans;
}
};
方法二:前缀树
class Trie {
private:
int wordVal;
vector<Trie*> next;
public:
Trie():next(26,nullptr),wordVal(0){};
// 插入,将结尾的bool 换成 int 就行
void insert(string& word, int val) {
Trie* node = this;
for(char ch : word) {
int index = ch - 'a';
if(node->next[index] == nullptr) {
node->next[index] = new Trie();
}
node = node->next[index];
}
node->wordVal = val;
}
// 先顺着前缀搜索下去,后面转而遍历每一种可能
void searchPrefix(string& word, int& ans) {
Trie* node = this;
// 找到前缀的最后一个字符
for(auto& ch : word) {
// 没有word这个前缀就直接返回
if(node->next[ch - 'a'] == nullptr)
return ;
node = node->next[ch - 'a'];
}
// 从这个字符开始遍历
dfs(node, ans);
}
void dfs(Trie* node, int& ans) {
if(node == nullptr) return ;
// 如果是单词就加上
if(node->wordVal > 0)
ans += node->wordVal;
// 遍历前缀剩余可能的部分
for(int i = 0; i < 26; i++) {
dfs(node->next[i], ans);
}
}
};
class MapSum {
public:
Trie* node;
MapSum() {
node = new Trie();
}
void insert(string key, int val) {
node->insert(key, val);
}
int sum(string prefix) {
int ans = 0;
node->searchPrefix(prefix, ans);
return ans;
}
};
067. 最大的异或
题目:
示例:
提示:
1 <= nums.length <= 2 * 104
0 <= nums[i] <= 231 - 1
思路:
class Trie {
public:
vector<Trie*> next;
Trie():next(2,nullptr){}
void insert(int x) {
Trie* node = this;
for(int i = 30; i >= 0; i--) {
// 取x的第i位
int u = (x >> i) & 1;
if(node->next[u] == nullptr) {
node->next[u] = new Trie();
}
node = node->next[u];
}
}
int search(int x) {
Trie* node = this;
// 存储 x 能够异或的最大值
int res = 0;
for(int i = 30; i >= 0; i--) {
int u = (x >> i) & 1;
// 优先选择取该位反方向为最大 因为 1 ^ 0 = 1
if(node->next[!u]) {
node = node->next[!u];
res = (res << 1) ^ !u;
}else {
// 没有的话取本身
node = node->next[u];
res = (res << 1) ^ u;
}
}
// ans = res ^ x
res ^= x;
return res;
}
};
class Solution {
public:
int findMaximumXOR(vector<int>& nums) {
Trie* node = new Trie();
for(int i : nums) {
node->insert(i);
}
int res = 0;
for(int i : nums) {
res = max(res, node->search(i));
}
return res;
}
};