0
点赞
收藏
分享

微信扫一扫

LeetCode 剑指 Offer II 前缀树 专题总结

往期文章 :

  • 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
  • wordprefix 仅由小写英文字母组成
  • insertsearchstartsWith 调用次数 总计 不超过 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 之前调用一次
  • 最多调用 100search

思路:

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);
    }
};

举报

相关推荐

0 条评论