【C/C++】745. 前缀和后缀搜索

142 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第24天,点击查看活动详情


题目链接:745. 前缀和后缀搜索

题目描述

设计一个包含一些单词的特殊词典,并能够通过前缀和后缀来检索单词。

实现 WordFilter 类:

  • WordFilter(string[] words) 使用词典中的单词 words 初始化对象。
  • f(string pref, string suff) 返回词典中具有前缀 prefix 和后缀 suff 的单词的下标。如果存在不止一个满足要求的下标,返回其中 最大的下标 。如果不存在这样的单词,返回 -1

提示:

  • 1words.length1041 \leqslant words.length \leqslant 10^4
  • 1words[i].length71 \leqslant words[i].length \leqslant 7
  • 1pref.length,suff.length71 \leqslant pref.length, suff.length \leqslant 7
  • words[i]prefsuff 仅由小写英文字母组成
  • 最多对函数 f 执行 10410^4 次调用

示例 1:

输入
["WordFilter", "f"]
[[["apple"]], ["a", "e"]]
输出
[null, 0]
解释
WordFilter wordFilter = new WordFilter(["apple"]);
wordFilter.f("a", "e"); // 返回 0 ,因为下标为 0 的单词:前缀 prefix = "a" 且 后缀 suff = "e"

整理题意

题目给定一组单词 words,然后每次通过给定前缀 prefix 和后缀 suff 进行查询 words 中是否存在同时具有给定前缀 prefix 和后缀 suff 的单词,如果存在多个满足条件的单词,返回下标最大的一个,如果不存在这样的单词,返回 -1

解题思路分析

首先观察题目数据范围,由于每个单词长度不超过 7,同时单词数量不会超过 10410^4 个,那么我们可以通过预处理出每个单词可能出现的前后缀组合作为键进行存储,对应的最大下标作为值存储哈希表中。在查询的时候,直接在哈希表中进行搜索是否存在这样的前后缀组合即可。

具体实现

  1. 为了区分开前后缀,避免混淆,我们在预处理前后缀组合的时候使用特殊字符进行连接。
  2. 同样在查询的时候也使用特殊字符连接前后缀后进行查询。
  3. 如果没有查询到返回 -1

复杂度分析

  • 时间复杂度:初始化消耗 O(i=0n1wi3)O(\sum\limits_{i=0}^{n-1}w_i^3) 时间,其中 wiw_i 是每个单词的字符数。每次检索消耗 O(p+s)O(p + s),其中 ps 分别是输入的 prefsuff 的长度。
  • 空间复杂度:初始化消耗 O(i=0n1wi3)O(\sum\limits_{i=0}^{n-1}w_i^3) 空间,每次检索消耗 O(p+s)O(p + s) 空间。

代码实现

class WordFilter {
private:
    unordered_map<string, int> mp;
public:
    WordFilter(vector<string>& words) {
        mp.clear();
        //将每个单词可能的前后缀组合放入map中
        int n = words.size();
        for(int i = 0; i < n; i++){
            int m = words[i].length();
            //枚举前缀长度 [0, m];
            for(int j = 0; j <= m; j++){
                string pre = "";
                for(int p = 0; p < j; p++) pre += words[i][p];
                //枚举后缀长度 [0, m];
                for(int k = 0; k <= m; k++){
                    string sub = "";
                    for(int p = m - k; p < m; p++) sub += words[i][p];
                    string key = pre + '#' + sub;
                    mp[key] = i;
                }
            }
        }
    }
    
    int f(string pref, string suff) {
        string key = pref + '#' + suff;
        return mp.count(key) == 0 ? -1 : mp[key];
    }
};

/**
 * Your WordFilter object will be instantiated and called as such:
 * WordFilter* obj = new WordFilter(words);
 * int param_1 = obj->f(pref,suff);
 */

总结

  • 该题的最优解为 字典树 解法,虽然暴力解法可以通过,但是在时间和空间复杂度上都要差于字典树解法。
  • 需要注意在暴力解法中前后缀需要用特殊字符划分开,不然会导致前后缀无法区分,而导致 abc # dab # cd# 区分前后缀)进行错误统计为一个。
  • 暴力解法可能因为网络波动原因导致卡超时。
  • 测试结果:

745.png

结束语

如果你还没有实现梦想,还没有过上想要的生活,那么你要做的不是怨天尤人,而是现在就开始行动。以积极的姿态迎接每一天,做好当下的每一件事,笃定向前的你定会品尝到生活的有滋有味。新的一天,加油!