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树节点。

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),由于所有操作都在原树组上进行,我们只需要几个下标变量,仅需要常数规模的空间存储。

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

相关推荐

  1. leetcode - 1268. Search Suggestions System

    2024-01-28 06:58:06       33 阅读
  2. ElasticSearch 搜索推荐

    2024-01-28 06:58:06       17 阅读
  3. LeetCode 1768. 交替合并字符串

    2024-01-28 06:58:06       16 阅读
  4. BOSS直聘推荐搜索系统工程师校招面经

    2024-01-28 06:58:06       32 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-01-28 06:58:06       14 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-01-28 06:58:06       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-01-28 06:58:06       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-01-28 06:58:06       18 阅读

热门阅读

  1. 力扣122双周赛

    2024-01-28 06:58:06       30 阅读
  2. 78.Go中的Timer 和 Ticker

    2024-01-28 06:58:06       23 阅读
  3. 阿里云云数据库RDS

    2024-01-28 06:58:06       27 阅读
  4. 通信协议的TCP/IP模型

    2024-01-28 06:58:06       28 阅读
  5. 最新2024年项目基金撰写与技巧及GPT融合应用

    2024-01-28 06:58:06       33 阅读
  6. WPF的ViewBox控件

    2024-01-28 06:58:06       34 阅读
  7. docker-compose离线安装

    2024-01-28 06:58:06       33 阅读
  8. Debian 12.x apt方式快速部署LNMP

    2024-01-28 06:58:06       24 阅读
  9. 03 创建图像窗口的几种方式

    2024-01-28 06:58:06       34 阅读