当前位置:   article > 正文

【LeetCode】1032. 字符流(hard)——字典树、自动机、前缀树、Trie树_leetcode 1032

leetcode 1032

【题目链接】

https://leetcode.cn/problems/stream-of-characters/

【题目大意】

给定一个单词数组和一个字符流,根据实时字符流的后缀查找单词数组中是否有匹配的单词,若有返回 t r u e true true ,否则返回 f a l s e false false

【输入示例】

输入:
[“StreamChecker”, “query”, “query”, “query”, “query”, “query”, “query”, “query”, “query”, “query”, “query”, “query”, “query”]
[[[“cd”, “f”, “kl”]], [“a”], [“b”], [“c”], [“d”], [“e”], [“f”], [“g”], [“h”], [“i”], [“j”], [“k”], [“l”]]
输出:
[null, false, false, false, true, false, true, false, false, false, false, false, true]
解释:
StreamChecker streamChecker = new StreamChecker([“cd”, “f”, “kl”]);
streamChecker.query(“a”); // 返回 False
streamChecker.query(“b”); // 返回 False
streamChecker.query(“c”); // 返回n False
streamChecker.query(“d”); // 返回 True ,因为 ‘cd’ 在 words 中
streamChecker.query(“e”); // 返回 False
streamChecker.query(“f”); // 返回 True ,因为 ‘f’ 在 words 中
streamChecker.query(“g”); // 返回 False
streamChecker.query(“h”); // 返回 False
streamChecker.query(“i”); // 返回 False
streamChecker.query(“j”); // 返回 False
streamChecker.query(“k”); // 返回 False
streamChecker.query(“l”); // 返回 True ,因为 ‘kl’ 在 words 中

【数据范围】

  • 1 <= words.length <= 2000
  • 1 <= words[i].length <= 200
  • words[i] 由小写英文字母组成
  • letter 是一个小写英文字母
  • 最多调用查询 4 ∗ 1 0 4 4 * 10^4 4104

模拟TLE

  既然是后缀匹配,那存在正确匹配的条件一定是字符流中的当前字符是某一单词的结束字母。因此,我们先按结束字母将单词分类存储,每次用字符流的当前字母向前构建单词,并查找以当前字母为结尾的单词列表。我们可以记录每个单词列表的最大单词长度,限制每次前向构建单词的最大长度。忽略初始化和哈希表的查找时间,理论上只需要 200 ∗ 4 ∗ 1 0 4 200*4 * 10^4 2004104,但是依然不在时间限制范围内,不出所料TLE了。

class StreamChecker {
public:
    map<char,set<string>> st;//根据尾字符将单词分组
    map<char,int> mp;//记录每个组里面单词的最大长度,也可以不要这个,默认单词最大长度200
    string ss;
    StreamChecker(vector<string>& words) {
        for(auto v:words){
            char c=v[v.length()-1];
            st[c].insert(v);
            if(mp[c]<=v.length()) mp[c]=v.length();
        }
    }
    
    bool query(char letter) {
        ss+=letter;
        auto s=st[letter];
        if(!s.empty()){//有以这个字符结尾的单词
        string w="";
            for(int i=0;i<mp[letter]&&i<ss.length();i++){//向前拼接新单词并搜索
                w=ss[ss.length()-1-i]+w;
                if(s.count(w)) return true;
            }
        }
        return false;
    }
};

/**
 * Your StreamChecker object will be instantiated and called as such:
 * StreamChecker* obj = new StreamChecker(words);
 * bool param_1 = obj->query(letter);
 */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

优化

  解题思路保持不变,但是优化查找过程。我们创建一个自动机,或者说前缀树。通过树的可达性来判断单词的存在与否。这棵前缀树包含两个属性,一个是子节点 c h i l d r e n [ 26 ] children[26] children[26] 和结束标志 i s E n d isEnd isEnd 。首先遍历每一个单词,每个单词从根节点向下延伸,在字典树中创建一条自己的路径并将结尾标志置为 t r u e true true 。每次查找时,也从这个树的根节点根据当前字符往下走,如果去到了不可达的地方,则不匹配,如果遇到上了 i s E n d = t r u e isEnd=true isEnd=true ,则匹配成功。这和有限自动状态机很相似,所以也叫AC自动机。具体细节看代码注释。

class StreamChecker {
public:
    struct Node{
        vector<Node*> children;
        bool isEnd;//重点标识
        Node():children(26),isEnd(false){}//初始化属性值,LeetCode不做这个会报错
    }; 
    string s;//字符流
    Node* trie=new Node();//树的根节点
    StreamChecker(vector<string>& words) {
        for(auto w:words){
            Node* node=trie;
            for(int i=w.size()-1;i>=0;i--){
                int idx=w[i]-'a';
                if(node->children[idx]==NULL) node->children[idx]=new Node();//如果不存在就创建一个节点
                node=node->children[idx];//存在则往下走
            }
            node->isEnd=true;//单词结束,设置终点
        }
    }
    
    bool query(char letter) {
        s+=letter;
        Node* node=trie;
        for(int i=0,j=s.size()-1;i<200&&j>=0;i++,j--){//结束条件为达到单词上限200或字符流遍历结束
            int idx=s[j]-'a';
            if(node->children[idx]==NULL) return false;//进入了不可达状态
            node=node->children[idx];
            if(node->isEnd) return true;//匹配成功
        }
        return false;
    }
};

/**
 * Your StreamChecker object will be instantiated and called as such:
 * StreamChecker* obj = new StreamChecker(words);
 * bool param_1 = obj->query(letter);
 */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/神奇cpp/article/detail/994667
推荐阅读
相关标签
  

闽ICP备14008679号