标签: 字典树

LeetCode刷题【面试题 17.17】多次搜索

给定一个较长字符串big和一个包含较短字符串的数组smalls,设计一个方法,根据smalls中的每一个较短字符串,对big进行搜索。输出smalls中的字符串在big里出现的所有位置positions,其中positions[i]smalls[i]出现的所有位置。

示例:

输入:
big = "mississippi"
smalls = ["is","ppi","hi","sis","i","ssippi"]
输出: [[1,4],[8],[],[3],[1,4,7,10],[5]]

提示:

  • 0 <= len(big) <= 1000
  • 0 <= len(smalls[i]) <= 1000
  • smalls的总字符数不会超过 100000。
  • 你可以认为smalls中没有重复字符串。
  • 所有出现的字符均为英文小写字母。
Related Topics
  • 字典树
  • 数组
  • 哈希表
  • 字符串
  • 字符串匹配
  • 滑动窗口

  • 👍 41
  • 👎 0
  • smalls数组构建字典树

    1. smalls数组构建字典树
    2. big数组从第i位开始到结尾的子序列拿到字典树上匹配,i从0开始分别求解,直到结束
    3. 因为匹配的时候需要知道当前字典树上字符串结束节点是哪个字符的,所以字典树的节点需要额外记录下各自归属与smalls数组中对应字符串的下标
    4. 在树上匹配的过程中,但凡匹配到了一个结束字符,就在结果数组的对应这个字符串下标的子数组中插入一个当前匹配的big字符串子序列的开始下标

    代码

    class Solution {
        public int[][] multiSearch(String big, String[] smalls) {
            Trie trie = new Trie();
            trie.smallsCount = smalls.length;
            trie.insert(smalls);
            return trie.search(big);
        }
    
    
        public class Trie{
    
            Node tree;
            int smallsCount = 0;
    
            public Trie(){
                tree = new Node();
            }
    
            void insert(String[] strArr){
                for (int idx = 0; idx < strArr.length; idx++) {
                    Node cur = tree;
                    String str = strArr[idx];
                    int i = -1;
                    while (++i < str.length()){
                        int child = str.charAt(i)-'a';
                        if (cur.children[child] == null){
                            cur.children[child] = new Node();
                        }
                        cur = cur.children[child];
                    }
                    cur.flag = true;
                    cur.smallIndex = idx;
                }
            }
    
            int[][] search(String str){
                int[][] res = new int[smallsCount][];
                List<List<Integer>> list = new ArrayList<>();
                for (int i = 0; i < smallsCount; i++) {
                    list.add(new ArrayList<>());
                }
    
                for (int i = 0; i < str.length(); i++) {
                    String subStr = str.substring(i);
                    Node cur = tree;
                    int idx = -1;
                    while (cur != null && ++idx < subStr.length()){
                        char c = subStr.charAt(idx);
                        if (cur.children[c-'a'] != null){
                            cur = cur.children[c-'a'];
                            if (cur.flag){
                                list.get(cur.smallIndex).add(i);
                            }
                        }else{
                            break;
                        }
                    }
                }
                for (int i = 0; i < list.size(); i++) {
                    res[i] = list.get(i).stream().mapToInt(Integer::intValue).toArray();
                }
                return res;
            }
    
    
    
    
            class Node{
                boolean flag = false;
                Node[] children;
                int smallIndex = -1;
                public Node(){
                    children = new Node[26];
                }
            }
        }
    }

    LeetCode刷题【1268】搜索推荐系统

    给你一个产品数组 products 和一个字符串 searchWord ,products  数组中每个产品都是一个字符串。

    请你设计一个推荐系统,在依次输入单词 searchWord 的每一个字母后,推荐 products 数组中前缀与 searchWord 相同的最多三个产品。如果前缀相同的可推荐产品超过三个,请按字典序返回最小的三个。

    请你以二维列表的形式,返回在输入 searchWord 每个字母后相应的推荐产品的列表。

     

    示例 1:

    输入:products = ["mobile","mouse","moneypot","monitor","mousepad"], searchWord = "mouse"
    输出:[
    ["mobile","moneypot","monitor"],
    ["mobile","moneypot","monitor"],
    ["mouse","mousepad"],
    ["mouse","mousepad"],
    ["mouse","mousepad"]
    ]
    解释:按字典序排序后的产品列表是 ["mobile","moneypot","monitor","mouse","mousepad"]
    输入 m 和 mo,由于所有产品的前缀都相同,所以系统返回字典序最小的三个产品 ["mobile","moneypot","monitor"]
    输入 mou, mous 和 mouse 后系统都返回 ["mouse","mousepad"]
    

    示例 2:

    输入:products = ["havana"], searchWord = "havana"
    输出:[["havana"],["havana"],["havana"],["havana"],["havana"],["havana"]]
    

    示例 3:

    输入:products = ["bags","baggage","banner","box","cloths"], searchWord = "bags"
    输出:[["baggage","bags","banner"],["baggage","bags","banner"],["baggage","bags"],["bags"]]
    

    示例 4:

    输入:products = ["havana"], searchWord = "tatiana"
    输出:[[],[],[],[],[],[],[]]
    

     

    提示:

    • 1 <= products.length <= 1000
    • 1 <= Σ products[i].length <= 2 * 10^4
    • products[i] 中所有的字符都是小写英文字母。
    • 1 <= searchWord.length <= 1000
    • searchWord 中所有字符都是小写英文字母。
    Related Topics
  • 字典树
  • 数组
  • 字符串

  • 👍 123
  • 👎 0
  • 字典树基本模板基本应用

    字典树基本模板先写出来,然后稍微改下就可以了

    1. String[] products遍历 构建字典树
    2. Node节点上保存对应的字符串,遍历到的时候直接把字符串塞过来,只保留3个,且按照字典序排序,这里借用到TreeSet结构的特性
    3. search的时候没遍历到一个节点就把对应Node上的字符串存下来
    4. 最终返回
    class Solution {
        List<List<String>> res;
        public List<List<String>> suggestedProducts(String[] products, String searchWord) {
            Trie trie = new Trie();
            for (String product : products) {
                trie.insert(product);
            }
            return trie.search(searchWord);
        }
    
        class Trie{
    
            Node root;
    
            public Trie(){
                root = new Node();
            }
    
            public void insert(String word){
                int idx = -1;
                Node cur = root;
                while (++idx < word.length()){
                    int i = word.charAt(idx) - 'a';
                    if (cur.children[i] == null){
                        cur.children[i] = new Node();
                    }
                    cur = cur.children[i];
                    cur.addWord(word);
                }
                cur.flag = true;
            }
    
            public List<List<String>> search(String word){
                List<List<String>> res = new ArrayList<>();
                int idx = -1;
                Node cur = root;
                while (++idx < word.length()){
                    List<String> list = new ArrayList<>();
                    int i = word.charAt(idx)-'a';
                    if (cur!=null){
                        cur = cur.children[i];
                        list = cur==null?new ArrayList<>():new ArrayList<>(cur.treeSet);
                    }
                    res.add(list);
                }
                return res;
            }
    
            class Node{
                boolean flag;
                Node[] children;
                TreeSet<String> treeSet;
    
                public Node(){
                    flag = false;
                    children = new Node[26];
                    treeSet = new TreeSet<>();
                }
    
                public void addWord(String word){
                    treeSet.add(word);
                    if (treeSet.size()>3){
                        treeSet.pollLast();
                    }
                }
            }
        }
    }

    LeetCode刷题【208】实现 Trie (前缀树)

    Trie(发音类似 “try”)或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。

    请你实现 Trie 类:

    • Trie() 初始化前缀树对象。
    • void insert(String word) 向前缀树中插入字符串 word
    • boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false
    • boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否则,返回 false

     

    示例:

    输入
    ["Trie", "insert", "search", "search", "startsWith", "insert", "search"]
    [[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
    输出
    [null, null, true, false, true, null, true]
    
    解释
    Trie trie = new Trie();
    trie.insert("apple");
    trie.search("apple");   // 返回 True
    trie.search("app");     // 返回 False
    trie.startsWith("app"); // 返回 True
    trie.insert("app");
    trie.search("app");     // 返回 True
    

     

    提示:

    • 1 <= word.length, prefix.length <= 2000
    • wordprefix 仅由小写英文字母组成
    • insertsearchstartsWith 调用次数 总计 不超过 3 * 104
    Related Topics
  • 设计
  • 字典树
  • 哈希表
  • 字符串

  • 👍 1209
  • 👎 0
  • 字典树基本模板操作

    class Trie {
    
        Node root = new Node();
    
        public Trie() {
    
        }
        
        public void insert(String word) {
            int idx = -1;
            Node cur = root;
            while (++idx < word.length()){
                if (cur.children[word.charAt(idx)-'a'] == null){
                    cur.children[word.charAt(idx)-'a'] = new Node();
                }
                cur = cur.children[word.charAt(idx)-'a'];
            }
            cur.flag = true;
        }
        
        public boolean search(String word) {
            int idx = -1;
            Node cur = root;
            while (++idx < word.length()){
                if (cur.children[word.charAt(idx)-'a'] != null){
                    cur = cur.children[word.charAt(idx)-'a'];
                }else{
                    return false;
                }
            }
            return cur.flag;
        }
        
        public boolean startsWith(String prefix) {
            int idx = -1;
            Node cur = root;
            while (++idx < prefix.length()){
                if (cur.children[prefix.charAt(idx)-'a'] != null){
                    cur = cur.children[prefix.charAt(idx)-'a'];
                }else{
                    return false;
                }
            }
            return true;
        }
    
    
        class Node{
            boolean flag = false;
            Node[] children = new Node[26];
            public Node(){}
        }
    }

    LeetCode刷题【211】添加与搜索单词 – 数据结构设计

    请你设计一个数据结构,支持 添加新单词 和 查找字符串是否与任何先前添加的字符串匹配 。

    实现词典类 WordDictionary

    • WordDictionary() 初始化词典对象
    • void addWord(word)word 添加到数据结构中,之后可以对它进行匹配
    • bool search(word) 如果数据结构中存在字符串与 word 匹配,则返回 true ;否则,返回  falseword 中可能包含一些 '.' ,每个 . 都可以表示任何一个字母。

     

    示例:

    输入:
    ["WordDictionary","addWord","addWord","addWord","search","search","search","search"]
    [[],["bad"],["dad"],["mad"],["pad"],["bad"],[".ad"],["b.."]]
    输出:
    [null,null,null,null,false,true,true,true]
    
    解释:
    WordDictionary wordDictionary = new WordDictionary();
    wordDictionary.addWord("bad");
    wordDictionary.addWord("dad");
    wordDictionary.addWord("mad");
    wordDictionary.search("pad"); // return False
    wordDictionary.search("bad"); // return True
    wordDictionary.search(".ad"); // return True
    wordDictionary.search("b.."); // return True
    

     

    提示:

    • 1 <= word.length <= 500
    • addWord 中的 word 由小写英文字母组成
    • search 中的 word 由 '.' 或小写英文字母组成
    • 最多调用 50000addWordsearch
    Related Topics
  • 深度优先搜索
  • 设计
  • 字典树
  • 字符串

  • 👍 403
  • 👎 0
  • 【第一次写字典树】Java, 添加与搜索单词

    第一次写字典树,之前也遇到过相关题目,知道有字典树这么个概念,但是根据学习计划,一直都还没接触。正好本周开始接触字符串相关算法了,才学习了KMP、Sunday、Shift And,正好遇到这题直接第一反应就是字典树。
    在做题之前写去简单学习了下相关算法,按照本题题意,在没有`.`的时候写了第一版字典树算法

    class WordDictionary {
    
        TrieNode trieTree;
    
        public WordDictionary() {
            trieTree = new TrieNode('\u0000');
        }
    
        public void addWord(String word) {
            if (null == word || word.length() < 1){
                return;
            }
            int idx = -1;
            TrieNode cur = trieTree;
            while (++idx < word.length()){
                if (!cur.children.containsKey(word.charAt(idx))){
                    cur.children.put(word.charAt(idx),new TrieNode(word.charAt(idx)));
                }
                cur = cur.children.get(word.charAt(idx));
            }
            cur.isTail = true;
        }
    
        public boolean search(String word) {
            int idx = -1;
            TrieNode cur = trieTree;
            while (++idx < word.length()){
                if (cur.children.containsKey(word.charAt(idx))){
                    cur = cur.children.get(word.charAt(idx));
                }else{
                    return false;
                }
            }
            return cur.isTail;
        }
    
    
    
    
        class TrieNode {
            char value;
            boolean isTail;
            HashMap<Character,TrieNode> children;
    
            public TrieNode(char value) {
                this.value = value;
                children = new HashMap<>();
            }
        }
    }

    写完了上面的,再来看这题,比一般匹配中多了一个`.`的可能,如果遇到`.`则表示可以是任意字符,那么相应的,最直观的作法,我们应当匹配下当前`TrieNode`下的`children`中的每一个节点。

    在写代码之前

    1. `children`结构一开始依旧是使用的`Hashmap`,虽然最终结果能跑通,但是性能上还是有点差距,后来看了下,对应的节点值只会是小写字母,也就是说范围限定死了,那么我们可以把`children`结构改为`TrieNode[]`数组,对应的key从0-25开始映射,这是一个小技巧,在部分情况下用数组代替`Hashmap`能有效提升不少性能,
    2. 对于搜索部分,原本是`cur.children.get(word.charAt(idx))`,现在是不确定多少了,所以分情况判断,如果当前字符是`.`,则遍历children处理,如果不是则只处理对应的明确的那个节点。
    3. 终止判断。
        - 情况1:如果已经到了被搜索字符串结尾,而当前对应的节点不是一个结束节点,则返回false;
        - 情况2:如果已经到了被搜索字符串结尾,且当前对应节点是一个结束节点,返回true;
        - 情况3:在遍历过程中,寻找能和字符串匹配的下一个节点失败,返回false;
        - 情况4:分支已经走到结束了了,而字符串遍历还没结束

    那么,代码

    class WordDictionary {
    
        TrieNode trieTree;
    
        public WordDictionary() {
            trieTree = new TrieNode('\u0000');
        }
    
        public void addWord(String word) {
            if (null == word || word.length() < 1){
                return;
            }
            int idx = -1;
            TrieNode cur = trieTree;
            while (++idx < word.length()){
                int key = word.charAt(idx)-'a';
                if (cur.children[key] == null){
                    cur.children[key] = new TrieNode(word.charAt(idx));
                }
                cur = cur.children[key];
            }
            cur.isTail = true;
        }
    
        public boolean search(String word) {
            return innerSearch(word,0,trieTree);
        }
    
        private boolean innerSearch(String word, int wordIdx, TrieNode startNode){
            if (wordIdx == word.length() || null==startNode){
                return null==startNode? false:startNode.isTail;
            }
    
            if (word.charAt(wordIdx)=='.'){
                for (TrieNode child : startNode.children) {
                    if (innerSearch(word,wordIdx+1,child)){
                        return true;
                    }
                }
            }else if(innerSearch(word,wordIdx+1, startNode.children[word.charAt(wordIdx)-'a'])){
                    return true;
            }
    
            return false;
        }
    
    
        class TrieNode {
            char value;
            boolean isTail;
            TrieNode[] children;
    
            public TrieNode(char value) {
                this.value = value;
                children = new TrieNode[26];
            }
        }
    }

    跑了下结果,看起来勉强及格吧

    解答成功:
    执行耗时:40 ms,击败了67.98% 的Java用户
    内存消耗:49 MB,击败了55.66% 的Java用户

    今天第一次接触,有什么问题或者错误希望帮忙指出,感谢了

    LeetCode刷题【212】单词搜索 II

    给定一个 m x n 二维字符网格 board 和一个单词(字符串)列表 words,找出所有同时在二维网格和字典中出现的单词。

    单词必须按照字母顺序,通过 相邻的单元格 内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。

     

    示例 1:

    输入:board = [["o","a","a","n"],["e","t","a","e"],["i","h","k","r"],["i","f","l","v"]], words = ["oath","pea","eat","rain"]
    输出:["eat","oath"]
    

    示例 2:

    输入:board = [["a","b"],["c","d"]], words = ["abcb"]
    输出:[]
    

     

    提示:

    • m == board.length
    • n == board[i].length
    • 1 <= m, n <= 12
    • board[i][j] 是一个小写英文字母
    • 1 <= words.length <= 3 * 104
    • 1 <= words[i].length <= 10
    • words[i] 由小写英文字母组成
    • words 中的所有字符串互不相同
    Related Topics
  • 字典树
  • 数组
  • 字符串
  • 回溯
  • 矩阵

  • 👍 457
  • 👎 0
  • 题解,今日份的每日一题 9月16日

    嗯,就回溯,没啥复杂的其实,

    class Solution {
    
        char[][] board;
        int colLimit;
        int rowLimit;
        List<String> res;
        HashSet<String> wordsSet;
        boolean[][] visited;
        int[][] directions = new int[][]{{0,1},{1,0},{0,-1},{-1,0}};
    
    
        public List<String> findWords(char[][] board, String[] words) {
            this.board = board;
            colLimit = board[0].length-1;
            rowLimit = board.length-1;
            res = new ArrayList<>();
            visited = new boolean[board.length][board[0].length];
            wordsSet = new HashSet<>();
            HashMap<Character,List<int[]>> wordsBegin = new HashMap<>();
            for (String word : words) {
                wordsSet.add(word);
                if (!wordsBegin.containsKey(word.charAt(0))){
                    wordsBegin.put(word.charAt(0),new ArrayList<>());
                }
            }
            for (int row = 0; row < board.length; row++) {
                for (int col = 0; col < board[row].length; col++) {
                    if (wordsBegin.containsKey(board[row][col])){
                        wordsBegin.get(board[row][col]).add(new int[]{row,col});
                    }
                }
            }
    
            wordsBegin.forEach((character, ints) -> {
                for (int[] anInt : ints) {
    //                System.out.println(anInt[0]+" "+anInt[1]);
                    List<Character> charArr = new ArrayList<>();
                    charArr.add(board[anInt[0]][anInt[1]]);
                    visited[anInt[0]][anInt[1]] = true;
                    dfs(anInt, charArr);
                    visited[anInt[0]][anInt[1]] = false;
                }
            });
            return res;
        }
    
    
        public void dfs(int[] position ,List<Character> charArr){
            if (charArr.size()==11){
                return;
            }
            char[] charArrCopy = new char[charArr.size()];
            for (int i = 0; i < charArr.size(); i++) {
                charArrCopy[i] = charArr.get(i);
            }
            String tmpStr = new String(charArrCopy);
    //        System.out.println(new String(charArrCopy));
            if (wordsSet.size()>0 && wordsSet.contains(tmpStr)){
                res.add(tmpStr);
                wordsSet.remove(tmpStr);
            }
            for (int[] direction : directions) {
                int[] target = new int[]{position[0]+direction[0],position[1]+direction[1]};
                if ( target[0] < 0 || target[0] > rowLimit || target[1] < 0 || target[1] > colLimit || visited[target[0]][target[1]]){
                    continue;
                }
                charArr.add(board[target[0]][target[1]]);
                visited[target[0]][target[1]] = true;
                dfs(target,charArr);
                charArr.remove(charArr.size()-1);
                visited[target[0]][target[1]] = false;
            }
        }
    }

    字典树的作法没太看懂,还要再看下研究下,然后这个回溯的,在回溯查找之前其实还可以再优化下,不应当把拼成的字符串到wordsSet中去查找,要查找的话必须要转成字符串,这样new 一个String对象的过程消耗不少,所以可以尝试,把wordsSet的结果转换成一个数据结构,可以快速查找到当前已经遍历的char数组是否能在这个结构中查找到。

    这个回头再看下怎么弄,我设想的这个结构感觉可能就是类似字典树吧,我猜。