Leetcode 1268 搜索推荐系统

题目信息

LeetoCode地址: 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

题目理解

这道题的题意不难理解,在我们使用搜索引擎的每一天都会遇到,不需要输入完整的关键词,哪怕仅仅只输入一个字,搜索引擎就会自动返回若干以这个字开头的若干查询结果,比如最近爆火的《繁花》电视剧

该题目是搜索引擎的极致简化版,每个字符都是a-z这26个小写字母。

如果你做过Trie 树相关的题目,很容就能联想到该题目可以以同样的方式求解,当我们建立好Trie 树之后,只要按照searchWord的每个字符进行逐个匹配直到最后一个字符,这时在按字典序查找最多3个以该字符为前缀的终止字符串。

这里就不对过程详细展开了,需要的同学可以回顾下面两篇文章。

实现Trie 字典树:CSDN

添加与搜索单词:CSDN

Trie树 写法

详细过程就不论述了,需要注意的是,在从第1个字符逐次递增一个字符进行搜索时,每一次都可以从上一次结尾的Trie 节点开始,而无需每次都从第0个字符开始,以节省时间。

在products树组长度为n,每个product字符串平均长度为m,searchWord字符串长度为s的情况下:

搜索 时间复杂度: O(s *26^m),在每一次遍历节点是,理论最坏qingdfs最多会调用product字符串长度层,每次都会遍历26个字符。

额外空间复杂度:O(n*m), 用于存储Trie树节点。

java 复制代码
Trie root;

    public List<List<String>> suggestedProducts(String[] products, String searchWord) {
        root = new Trie();
        for (String product : products) {
            addWord(product);
        }
        List<List<String>> result = new ArrayList<>();
        Trie lastEndTrie = root;
        for (int i = 0; i< searchWord.length(); i++) {
            List<String> current =  new ArrayList<>();
            lastEndTrie = search(lastEndTrie, searchWord, i, current);
            result.add(current);
        }
        return result;
    }

    public Trie search(Trie lastEndTrie, String word, int index, List<String> list) {
        if (lastEndTrie == null) {
            return null;
        }
        int indexOfChar = word.charAt(index) - 'a';
        Trie next = lastEndTrie.nextList[indexOfChar];
        if (next != null) {
            lastEndTrie = next;
        } else {
            return null;
        }
        dfs(lastEndTrie, list);
        return lastEndTrie;
    }
    public void addWord(String word) {
        Trie current = root;
        for (char c : word.toCharArray()) {
            if (current.nextList[c-'a'] != null) {
                current = current.nextList[c-'a'];
            } else {
                Trie next = new Trie();
                current.nextList[c-'a'] = next;
                current = next;
            }
        }
        current.value = word;
        current.end = true;
    }

    private void dfs(Trie current, List<String> list) {
        if (list.size() == 3 || current == null) {
            return;
        }
        if (current.end) {
            list.add(current.value);
        }
        for (Trie trie : current.nextList) {
            if (trie != null) {
                dfs(trie, list);
            }
        }
    }

    class Trie {
        boolean end;

        String value;
        Trie[] nextList;
        public Trie() {
            this.end = false;
            this.nextList = new Trie[26];
        }
    }

排序+双指针 写法

其实当题目提到"按字典序返回最小的三个" 这样的描述时,我们就要很警觉了,因为很可能和与排序有关系。

假如我们先将products按照字典序进行排序,然后确定满足当前searchWord的product的左右下标区间,则该区间最左侧的三个元素就是题目要求的字典序最小的三个元素。

更妙的是,假设我们当前搜索匹配的字符串是searchWord的0到i字串,当我们递增i匹配0到i+1字传时,完全不需要从头确定products的左右下标,直接基于上一次遍历的下标基础上进行类似的缩小范围即可。

那么缩小范围的逻辑是什么呢?其实也是很直观的。

  • 初始来看,左下标l=0, 右下标r=products.length-1. 毫无疑问l<=r 是必须保证的
  • 其次,下标l对应的的product,其长度必须大于等于当前searchWord下标i,否则就要向右移动l. 当product[l]的第i个字符与searchWord的第i个字符不相符时,也不满足题目前缀的要求,也应该向右移动l.
  • 类似的,下标r对应的的product,其长度必须大于等于当前searchWord下标i,否则就要向左移动r. 当product[r]的第i个字符与searchWord的第i个字符不相符时,也不满足题目前缀的要求,也应该向左移动r.

当l和r位置在当前i的值下都确定之后,就可以将区间内的前三个元素放入结果集中,然后对i递增,进行下一轮的遍历,直到遍历完所有的字符。

在products树组长度为n,每个product字符串平均长度为m,searchWord字符串长度为s的情况下:

搜索 时间复杂度: O(n),l向右移动,r向左移动,直到相遇为止,最多会移动products的长度那么多步。

额外空间复杂度:O(1),由于所有操作都在原树组上进行,我们只需要几个下标变量,仅需要常数规模的空间存储。

java 复制代码
public List<List<String>> suggestedProducts(String[] products, String searchWord) {
        Arrays.sort(products);
        int l = 0, r = products.length-1;
        List<List<String>> result = new ArrayList<>();
        for (int i = 0; i < searchWord.length(); i++) {
            while (l < r && (products[l].length() <= i || products[l].charAt(i) != searchWord.charAt(i))) {
                l++;
            }
            while (l < r && (products[r].length() <= i || products[r].charAt(i) != searchWord.charAt(i))) {
                r--;
            }
            List<String> temp = new ArrayList<>();
            for (int ii = l; ii<=r && ii+3 <=r; ii++) {
                temp.add(products[ii]);
            }
            result.add(temp);
        }
        return result;
    }
相关推荐
繁依Fanyi6 分钟前
简易安卓句分器实现
java·服务器·开发语言·算法·eclipse
烦躁的大鼻嘎22 分钟前
模拟算法实例讲解:从理论到实践的编程之旅
数据结构·c++·算法·leetcode
C++忠实粉丝39 分钟前
计算机网络socket编程(4)_TCP socket API 详解
网络·数据结构·c++·网络协议·tcp/ip·计算机网络·算法
祁思妙想1 小时前
10.《滑动窗口篇》---②长度最小的子数组(中等)
leetcode·哈希算法
用户37791362947551 小时前
【循环神经网络】只会Python,也能让AI写出周杰伦风格的歌词
人工智能·算法
福大大架构师每日一题1 小时前
文心一言 VS 讯飞星火 VS chatgpt (396)-- 算法导论25.2 1题
算法·文心一言
EterNity_TiMe_1 小时前
【论文复现】(CLIP)文本也能和图像配对
python·学习·算法·性能优化·数据分析·clip
机器学习之心2 小时前
一区北方苍鹰算法优化+创新改进Transformer!NGO-Transformer-LSTM多变量回归预测
算法·lstm·transformer·北方苍鹰算法优化·多变量回归预测·ngo-transformer
yyt_cdeyyds2 小时前
FIFO和LRU算法实现操作系统中主存管理
算法
alphaTao2 小时前
LeetCode 每日一题 2024/11/18-2024/11/24
算法·leetcode