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. 当productl的第i个字符与searchWord的第i个字符不相符时,也不满足题目前缀的要求,也应该向右移动l.
  • 类似的,下标r对应的的product,其长度必须大于等于当前searchWord下标i,否则就要向左移动r. 当productr的第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;
    }
相关推荐
Jerry4 小时前
LeetCode 28. 找出字符串中第一个匹配项的下标
算法
Jerry6 小时前
LeetCode 459. 重复的子字符串
算法
海石9 小时前
1500分的题目,确实有实力,不过还是我略胜一筹
算法·leetcode
海石9 小时前
【记忆化搜索】条条大路通AC,走好适合你的那一条,走到后再考虑走得快
算法·leetcode
Jerry11 小时前
LeetCode 151. 反转字符串中的单词
算法
a11177613 小时前
LM 算法迭代过程动画演示(SLAM)
算法
头茬韭菜13 小时前
Context 的生死抉择:四层压缩、截断算法与 Session Memory
算法·ai
Jerry13 小时前
LeetCode 541. 反转字符串 II
算法
Jerry14 小时前
LeetCode 344. 反转字符串
算法
搞科研的小刘选手14 小时前
【香港大学主办&IEEE出版】第六届计算机视觉、应用与算法国际学术会议(CVAA 2026)
算法·计算机视觉·应用·学术会议