往期文章 :
- LeetCode 剑指 Offer II 链表 专题总结
- LeetCode 剑指 Offer II 哈希表 专题总结
- LeetCode 剑指 Offer II 栈 专题总结
- LeetCode 剑指 Offer II 队列 专题总结
- LeetCode 剑指 Offer II 树(上) 专题总结
- LeetCode 剑指 Offer II 树(下) 专题总结
- LeetCode 剑指 Offer II 堆 专题总结
第一题了解前缀树比较简单,后面两题应用就稍微难一点,代码量都挺高的,但思路很简单,希望别劝退新手
目录
062. 实现前缀树
题目:
示例:
提示:
1 <= word.length, prefix.length <= 2000
word
和prefix
仅由小写英文字母组成insert
、search
和startsWith
调用次数 总计 不超过3 * 104
次
思路:
class Trie {
private:
// 前缀树, 相当于26叉树
vector<Trie*> next;
// 判断是否为单词终点
bool isWrod;
// 因为search,startsWith方法都需要查询,所以提取出查询方法
// 直接返回查询最后的点
Trie* searchPrefix(string prefix) {
Trie* node = this;
for(char ch : prefix) {
int index = ch - 'a';
if(node->next[index] != nullptr) {
node = node->next[index];
}else {
return nullptr;
}
}
return node;
}
public:
Trie():next(26),isWrod(false) {}
void insert(string word) {
int count = 0;
// 指向当前类,每个类都有26个分支
Trie* node = this;
for(char ch : word) {
int index = ch - 'a';
if(node->next[index] == nullptr) {
// 创建父类节点的index节点,此时index也有26个分支
node->next[index] = new Trie();
}
// 将父类指向index节点
node = node->next[index];
}
// 创建完当前node指向最后一个单词,设为单词终点
node->isWrod = true;
}
// 不为空 and 最后一个点的isWrod为true 代表word存在
bool search(string word) {
Trie* node = this->searchPrefix(word);
return node != nullptr && node->isWrod;
}
// 不为空即查到了前缀
bool startsWith(string prefix) {
Trie* node = this->searchPrefix(prefix);
return node != nullptr;
}
};
063. 替换单词
题目:
示例:
提示:
1 <= dictionary.length <= 1000
1 <= dictionary[i].length <= 100
dictionary[i]
仅由小写字母组成。1 <= sentence.length <= 10^6
sentence
仅由小写字母和空格组成。sentence
中单词的总量在范围[1, 1000]
内。sentence
中每个单词的长度在范围[1, 1000]
内。sentence
中单词之间由一个空格隔开。sentence
没有前导或尾随空格。
思路:
class Trie {
private:
bool isWord;
vector<Trie*> next;
public:
Trie():next(26,nullptr),isWord(false){};
// 跟上一题一样
void insert(const string& word) {
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->isWord = true;
}
// 相对于上一题的search稍作修改
int prefixLen(const string& word) {
Trie* node = this;
// 返回前缀的长度
int len = 0;
for(char ch : word) {
int index = ch - 'a';
// 没有这个前缀
if(node->next[index] == nullptr) {
return 0;
}
node = node->next[index];
len++;
// 找到这个前缀并返回这个前缀的长度
if(node->isWord)
return len;
}
return 0;
}
};
class Solution {
public:
string replaceWords(vector<string>& dictionary, string sentence) {
Trie* root = new Trie();
// 将词根加入前缀树
for(auto& word : dictionary) {
root->insert(word);
}
// 分割字符串
vector<string> words{""};
for(char ch : sentence) {
if(ch == ' ') {
words.push_back("");
}else {
words.back().push_back(ch);
}
}
// 处理字符串
string res;
for(auto& word : words) {
int len = root->prefixLen(word);
// 没找到直接添加
if(len == 0){
res += word + " ";
}else {
// 找到前缀只添加前缀部分
res += word.substr(0, len) + " ";
}
}
res.pop_back();
return res;
}
};
064. 神奇的字典
题目:
示例:
提示:
1 <= dictionary.length <= 100
1 <= dictionary[i].length <= 100
dictionary[i]
仅由小写英文字母组成dictionary
中的所有字符串 互不相同1 <= searchWord.length <= 100
searchWord
仅由小写英文字母组成buildDict
仅在search
之前调用一次- 最多调用
100
次search
思路:
class Trie {
public:
vector<Trie*> next;
bool isWord;
Trie():next(26,nullptr),isWord(false){}
void insert(const string& word) {
Trie* node = this;
for(auto& ch : word) {
int index = ch - 'a';
if(node->next[index] == nullptr) {
node->next[index] = new Trie();
}
node = node->next[index];
}
node->isWord = true;
}
};
class MagicDictionary {
private:
Trie* root;
bool dfs(Trie* root, string& word, int index, int edit) {
if(root == nullptr || edit > 1) return false;
//如果root正好在字典字符串结尾,且index刚好到达字符串末尾,且只被修改过一个字符
if(root->isWord && index == word.size() && edit == 1) {
return true;
}
if(index >= word.size())
return false;
//必须都遍历,否则如果 hello, hallo 查询hello直接查询完,就不会返回true
for(int i = 0; i < 26; i++) {
//如果子节点存在,edit不变;否则,edit加1;
int nextEdit = i == word[index] - 'a'? edit : edit + 1;
// 遍历26种,直到返回true
if(dfs(root->next[i], word, index + 1, nextEdit))
return true;
}
return false;
}
public:
/** Initialize your data structure here. */
MagicDictionary() {
root = new Trie();
}
void buildDict(vector<string> dictionary) {
for(auto& word : dictionary) {
root->insert(word);
}
}
bool search(string searchWord) {
return dfs(root, searchWord, 0, 0);
}
};