Trie 类型的题目总结

16 篇文章 0 订阅

trie字典树可以用来查找单词或者搜索剪枝用。

Implement Trie (Prefix Tree) 实现一个 Trie,包含 insertsearch, 和 startsWith 这三个方法。(模板必须记住;没有儿子建立儿子,有儿子走儿子;)

class Trie {
    private class TrieNode {
        public TrieNode[] children;
        public boolean isword;
        public TrieNode () {
            this.children = new TrieNode[26];
            this.isword = false;
        }
    }

    /** Initialize your data structure here. */
    private TrieNode root;
    public Trie() {
        root = new TrieNode();
    }
    
    /** Inserts a word into the trie. */
    public void insert(String word) {
        if(word == null || word.length() == 0) {
            return;
        }
        TrieNode cur = root;
        for(int i = 0; i < word.length(); i++) {
            char c = word.charAt(i);
            if(cur.children[c - 'a'] == null) {
                cur.children[c - 'a'] = new TrieNode();
            }
            cur = cur.children[c - 'a'];
        }
        cur.isword = true;
    }
    
    /** Returns if the word is in the trie. */
    public boolean search(String word) {
        TrieNode cur = searchPrefix(word);
        return cur != null && cur.isword;
    }
    
    /** Returns if there is any word in the trie that starts with the given prefix. */
    public boolean startsWith(String prefix) {
       TrieNode cur = searchPrefix(prefix);
        return cur != null;
    }
    
    private TrieNode searchPrefix(String prefix) {
         if(prefix == null || prefix.length() == 0) {
            return null;
        }
        TrieNode cur = root;
        for(int i = 0; i < prefix.length(); i++) {
            char c = prefix.charAt(i);
            if(cur.children[c - 'a'] == null) {
                return null;
            }
            cur = cur.children[c - 'a'];
        }
        return cur;
    }
}

/**
 * Your Trie object will be instantiated and called as such:
 * Trie obj = new Trie();
 * obj.insert(word);
 * boolean param_2 = obj.search(word);
 * boolean param_3 = obj.startsWith(prefix);
 */

Add and Search Word - Data structure design (遇见 ‘.’ 之后,for循环check每一个可能性;注意这题我写了两个坑:

1. index == word.length()的时候,返回的是cur.isword, 而不是直接返回true;

2. for循环的时候,一定要判断cur.children[i] != null, 也就是判断存入的单词,是否这条路径;搜索所有的路径,那就是DFS搜索,每一种情况都要check,只要有一种情况是true,那么就返回true;)

思路:这道题如果做过之前的那道
Implement Trie (Prefix Tree) 实现字典树(前缀树)的话就没有太大的难度了,因为这道题里面'.'可以代替任意字符,所以一旦有了'.',就需要查找之前存下的所有下一层的不是null的path;String match 的题,一般都是DFS 参数里面加入index,然后递归 subproblem求解;

class WordDictionary {
    private class TrieNode {
        public TrieNode[] children;
        public boolean isword;
        public String word;
        public TrieNode() {
            this.children = new TrieNode[26];
            this.isword = false;
            this.word = null;
        }
    }
    
    private class Trie {
        public TrieNode root;
        public Trie() {
            this.root = new TrieNode();
        }
        
        public void insert(String word) {
            TrieNode cur = root;
            for(int i = 0; i < word.length(); i++) {
                char c = word.charAt(i);
                if(cur.children[c - 'a'] == null) {
                    cur.children[c - 'a'] = new TrieNode();
                }
                cur = cur.children[c - 'a'];
            }
            cur.isword = true;
            cur.word = word;
        }
        
        public boolean search(String word) {
            return ismatch(word, 0, root);
        }
        
        private boolean ismatch(String word, int index, TrieNode cur) {
            if(index == word.length()) {
                return cur.isword;
            }
            char c = word.charAt(index);
            if(c == '.') {
                for(int i = 0; i < 26; i++) {
                    if(cur.children[i] != null) { // 搜索存储的,下一层所有不是null的path;
                        if(ismatch(word, index + 1, cur.children[i])) {
                            return true;
                        }
                    }
                }
                return false;
            } else {
                if(cur.children[c - 'a'] == null) {
                    return false;
                } else {
                    return ismatch(word, index + 1, cur.children[c - 'a']);
                }
            }
        }
    }
    
    /** Initialize your data structure here. */
    private Trie trie;
    public WordDictionary() {
        trie = new Trie();
    }
    
    /** Adds a word into the data structure. */
    public void addWord(String word) {
        trie.insert(word);
    }
    
    /** Returns if the word is in the data structure. A word could contain the dot character '.' to represent any one letter. */
    public boolean search(String word) {
        return trie.search(word);   
    }
}

/**
 * Your WordDictionary object will be instantiated and called as such:
 * WordDictionary obj = new WordDictionary();
 * obj.addWord(word);
 * boolean param_2 = obj.search(word);
 */

Longest Word in Dictionary (思路1:这题标记为easy,有简单的方法,就是首先sort array,这样小的单词在前面,大的在后面,然后每个单词判断之前的substring(0, len - 1)是否在visited set里面,如果在,就表明之前的substring也是承接过来的,因为加入set的条件就是之前能够承接过来。思路2: 用trie做,可以做到O(n*L); 这题巧妙的是,build trie之后,可以bfs,层级的搜,要得到smallest lexicographical order.那么我最后搜集到的res,就是字典序最小的,因为是从后往前搜,然后最后一个答案就是最前面的;)

class Solution {
    private class TrieNode {
        public TrieNode[] children;
        public boolean isword;
        public String word;
        public TrieNode () {
            this.children = new TrieNode[26];
            this.isword = false;
            this.word = null;
        }
    }
    
    private class Trie {
        private TrieNode root;
        public Trie() {
            this.root = new TrieNode();
        }
        
        public void insert(String word) {
            TrieNode cur = root;
            for(int i = 0; i < word.length(); i++) {
                char c = word.charAt(i);
                if(cur.children[c - 'a'] == null) {
                    cur.children[c - 'a'] = new TrieNode();
                }
                cur = cur.children[c - 'a'];
            }
            cur.isword = true;
            cur.word = word;
        }
    }
    
    public String longestWord(String[] words) {
        if(words == null) {
            return null;
        }
        Trie trie = new Trie();
        for(String word: words) {
            trie.insert(word);
        }
        
        Queue<TrieNode> queue = new LinkedList<>();
        queue.offer(trie.root);
        
        String res = "";
        while(!queue.isEmpty()) {
            int size = queue.size();
            TrieNode node = null;
            for(int i = 0; i < size; i++) {
                node = queue.poll();
                // get first node as res; lex order is smallest;
                if(i == 0 && node.isword) {
                    res = node.word;
                }
                // get next level;
                for(int j = 0; j < 26; j++) {
                    if(node.children[j] != null && node.children[j].isword) {
                        queue.offer(node.children[j]);
                    }
                }
            }
        }
        return res;
    }
}

====== Trie + DFS + BackTracking 搜索,这样的题,不少,出的也是很好的!=====

Word Search II (其实传递trietree进去,就已经相当于把所有的word全部传进去了,那么对于每个 x,y,那么只需要在这个点展开,搜索所有可能的string就可以了。参数传递不需要用word,只需要用trietree,因为是搜所有可能的word;

class Solution {
    public class TrieNode {
        public TrieNode[] children;
        public boolean isword;
        public String word;
        public TrieNode () {
            this.children = new TrieNode[26];
            this.isword = false;
            this.word = null;
        }
    }
    
    public class Trie {
        public TrieNode root;
        public Trie() {
            this.root = new TrieNode();
        }
        public void insertWord(String word) {
            TrieNode cur = root;
            for(int i = 0; i < word.length(); i++) {
                char c = word.charAt(i);
                if(cur.children[c - 'a'] == null) {
                    cur.children[c - 'a'] = new TrieNode();
                }
                cur = cur.children[c - 'a'];
            }
            cur.isword = true;
            cur.word = word;
        }
    }
    
    public List<String> findWords(char[][] board, String[] words) {
        Trie trie = new Trie();
        for(String word: words) {
            trie.insertWord(word);
        }
        int m = board.length;
        int n = board[0].length;
        HashSet<String> set = new HashSet<>();
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                boolean[][] visited = new boolean[m][n];
                dfs(trie.root, board, i, j, visited, set);
            }
        }
        return new ArrayList<String>(set);
    }
    
    int[][] dirs = {{0,1},{0,-1},{-1,0},{1,0}};
    private void dfs(TrieNode cur, char[][] board, int x, int y, boolean[][] visited, HashSet<String> set) {
        if(x < 0 || x >= board.length || y < 0 || y >= board[0].length || visited[x][y]) {
            return;
        }
        char c = board[x][y];
        if(cur.children[c - 'a'] == null) {
            return;
        }
        cur = cur.children[c - 'a'];
        if(cur.isword) {
            set.add(cur.word);
        }
        visited[x][y] = true;
        for(int[] dir: dirs) {
            int nx = x + dir[0];
            int ny = y + dir[1];
            dfs(cur, board, nx, ny, visited, set);
        }
        visited[x][y] = false;
    }
}

Concatenated Words 思路:先把单词insert into trie,好判断是否包含一个string,然后用trie 的root,来做DFS,每次找到一个单词,继续递归,index前进,一直到word.length() 同时count >=2代表是我想要的;注意:cur还是在走,但是每次是要从root开始搜单词;cur = root;

class Solution {
    private class TrieNode {
        public TrieNode[] children;
        public boolean isword;
        public String word;
        public TrieNode () {
            this.children = new TrieNode[26];
            this.isword = false;
            this.word = null;
        }
    }
    
    private class Trie {
        public TrieNode root;
        public Trie() {
            this.root = new TrieNode();
        }
        
        public void insert(String word) {
            TrieNode cur = root;
            for(int i = 0; i < word.length(); i++) {
                char c = word.charAt(i);
                if(cur.children[c - 'a'] == null) {
                   cur.children[c - 'a'] = new TrieNode();
                }
                cur = cur.children[c - 'a'];
            }
            cur.isword = true;
            cur.word = word;
        }
    }
    
    public List<String> findAllConcatenatedWordsInADict(String[] words) {
        List<String> res = new ArrayList<String>();
        Trie trie = new Trie();
        for(String word: words) {
            trie.insert(word);
        }
        
        for(String word: words) {
            if(dfs(word, trie.root, 0, 0)) {
                res.add(word);
            }
        }
        return res;
    }
    
    private boolean dfs(String word, TrieNode root, int index, int count) {
        if(index == word.length()) {
            return count >= 2;
        }
        
        TrieNode cur = root;
        for(int i = index; i < word.length(); i++) {
            char c = word.charAt(i);
            if(cur.children[c - 'a'] == null) {
                return false;
            }
            cur = cur.children[c - 'a'];
            // 如果当前是一个单词了,那么继续下一层的递归,如果是true,就返回true;
            if(cur.isword) {
                if(dfs(word, root, i + 1, count + 1)) {
                    return true;
                }
            }
        }
        return false;
    }
}

Search Suggestions System (就是每个node存list of word,然后每次type word的时候,sort list,取走前面三个就是答案)

class Solution {
    class TrieNode {
        public TrieNode[] children;
        public boolean isword;
        public List<String> list;
        public TrieNode () {
            this.children = new TrieNode[26];
            this.isword = false;
            this.list = new ArrayList<>();
        }
    }
    
    class Trie {
        public TrieNode root;
        public Trie() {
            this.root = new TrieNode();
        }
        
        public void insertWord(String word) {
            TrieNode cur = root;
            for(int i = 0; i < word.length(); i++) {
                char c = word.charAt(i);
                if(cur.children[c - 'a'] == null) {
                    cur.children[c - 'a'] = new TrieNode();
                }
                cur = cur.children[c - 'a'];
                cur.list.add(word);
            }
        }
    }
    
    public List<List<String>> suggestedProducts(String[] products, String searchWord) {
        List<List<String>> lists = new ArrayList<List<String>>();
        Trie trie = new Trie();
        for(String product: products) {
            trie.insertWord(product);
        }
        
        TrieNode cur = trie.root;
        for(int i = 0; i < searchWord.length(); i++) {
            char c = searchWord.charAt(i);
            List<String> list = new ArrayList<>();
            if(cur != null && cur.children[c - 'a'] != null) {
                cur = cur.children[c - 'a'];
                List<String> temp = cur.list;
                Collections.sort(temp);
                for(int k = 0; k < 3 && k < temp.size(); k++) {
                    list.add(temp.get(k));
                }
                lists.add(list);
            } else {
                cur = null;
                lists.add(list);
            }
        }
        return lists;
    }
}

Design Search Autocomplete System (跟上面的 Search Suggestions System很像,就是cur如果是null了,以后都是null了,这题不同的是,输入input的c,最后sb.toString()也要加入频率,所以trie node里面存string和频率,这题是用的hashmap来表示的children)

class AutocompleteSystem {
    private class TrieNode {
        public HashMap<Character, TrieNode> children; // 因为有空格;所以用hashmap,这样也节约空间;
        public boolean isword;
        public HashMap<String, Integer> freqMap;
        public TrieNode () {
            this.children = new HashMap<>();
            this.isword = false;
            this.freqMap = new HashMap<>();
        }
    }
    
    private class Trie {
        public TrieNode root;
        public Trie() {
            this.root = new TrieNode();
        }
        
        public void insert(String word, int time) {
            TrieNode cur = root;
            for(int i = 0; i < word.length(); i++) {
                char c = word.charAt(i);
                if(cur.children.get(c) == null) {
                    cur.children.put(c, new TrieNode());
                }
                cur = cur.children.get(c);
                // 一定是沿途的trieNode都要记录,而不是最后才记录;
                cur.freqMap.put(word, cur.freqMap.getOrDefault(word, 0) + time);
            }
            cur.isword = true;
        }
    }
    
    private class Node {
        public String word;
        public int freq;
        public Node(String word, int freq) {
            this.word = word;
            this.freq = freq;
        }
    }

    private Trie trie;
    private TrieNode cur;
    private StringBuilder sb;
    public AutocompleteSystem(String[] sentences, int[] times) {
        trie = new Trie();
        for(int i = 0; i < sentences.length; i++) {
            trie.insert(sentences[i], times[i]);
        }
        cur = trie.root;
        sb = new StringBuilder();
    }
    
    public List<String> input(char c) {
        List<String> res = new ArrayList<>();
        if(c == '#') {
            String lastword = sb.toString();
            trie.insert(lastword, 1);
            cur = trie.root;
            sb = new StringBuilder(); // 记住,sb需要还原;
        } else {
            sb.append(c);
            if(cur != null && cur.children.get(c) != null) {
                cur = cur.children.get(c);
                PriorityQueue<Node> pq = new PriorityQueue<Node>((a, b) -> (
                    a.freq != b.freq ? b.freq - a.freq : a.word.compareTo(b.word)));
                for(String word: cur.freqMap.keySet()) {
                    pq.offer(new Node(word, cur.freqMap.get(word)));
                }
                
                int count = 0;
                while(count < 3 && !pq.isEmpty()) {
                    res.add(pq.poll().word);
                    count++;
                }
            } else {
                cur = null;
            }
        }
        return res;
    }
}

/**
 * Your AutocompleteSystem object will be instantiated and called as such:
 * AutocompleteSystem obj = new AutocompleteSystem(sentences, times);
 * List<String> param_1 = obj.input(c);
 */

Word Squares (如何利用trie剪枝就是通过已经加入的单词list,构造出后面一定要出现的prefix是什么,也就是下一个string,开头必须是以已经输入的string的接下来的char组成的prefix开始,然后从trie中node的prefix list中选取candidate;)

class Solution {
    private class TrieNode {
        public TrieNode[] children;
        public boolean isword;
        public List<String> list;
        public TrieNode () {
            this.children = new TrieNode[26];
            this.isword = false;
            this.list = new ArrayList<String>();
        }
    }
    
    private class Trie {
        public TrieNode root;
        public Trie() {
            this.root = new TrieNode();
        }
        
        public void insert(String word) {
            TrieNode cur = root;
            for(int i = 0; i < word.length(); i++) {
                char c = word.charAt(i);
                if(cur.children[c - 'a'] == null) {
                    cur.children[c - 'a'] = new TrieNode();
                }
                cur = cur.children[c - 'a'];
                cur.list.add(word);
            }
            cur.isword = true;
        }
        
        public List<String> getPrefix(String prefix) {
            TrieNode cur = root;
            List<String> res = new ArrayList<String>();
            for(int i = 0; i < prefix.length(); i++) {
                char c = prefix.charAt(i);
                if(cur.children[c - 'a'] == null) {
                    return res;
                }
                cur = cur.children[c - 'a'];
            }
            res.addAll(cur.list);
            return res;
        }
    }
    
    public List<List<String>> wordSquares(String[] words) {
        List<List<String>> lists = new ArrayList<List<String>>();
        Trie trie = new Trie();
        for(String word: words) {
            trie.insert(word);
        }
        
        int n = words[0].length();
        List<String> list = new ArrayList<String>();
        for(String word: words) {
            list.add(word); // 这里必须加入第一个单词,然后try所有的可能性;
            dfs(words, lists, list, trie, n);
            list.remove(list.size() - 1);
        }
        return lists;
    }
    
    private void dfs(String[] words, List<List<String>> lists, 
                    List<String> list, Trie trie, int n) {
        if(n == list.size()) {
            lists.add(new ArrayList<String>(list));
            return;
        }
        StringBuilder sb = new StringBuilder();
        int index = list.size(); // 如果有两个单词,那么用前两个单词的第三个位子,拼凑成prefix;
        for(int i = 0; i < list.size(); i++) {
            sb.append(list.get(i).charAt(index));
        }
        // 从prefix的list中去选择;
        for(String candidate: trie.getPrefix(sb.toString())) {
            list.add(candidate);
            dfs(words, lists, list, trie, n);
            list.remove(list.size() - 1);
        }
    }
}

Palindrome Pairs (这题用hashmap,判断 reverseStr2 + str1 + str2,  str1 + str2 + reverseStr1来做,注意要判断map.get(reverseStr1) != i)  防止自己是自己的palindrome,把自己加两遍的情况); 这题不是用trie做,用hashmap,像two sum做。

class Solution {
    public List<List<Integer>> palindromePairs(String[] words) {
        List<List<Integer>> lists = new ArrayList<List<Integer>>();
        if(words == null || words.length == 0) {
            return lists;
        }
        HashMap<String, Integer> hashmap = new HashMap<>();
        for(int i = 0; i < words.length; i++) {
            hashmap.put(words[i], i);
        }
        
        for(int i = 0; i < words.length; i++) {
            String word = words[i];
            for(int j = 0; j <= word.length(); j++) {
                String str1 = word.substring(0, j);
                String str2 = word.substring(j);
                if(isPalindrome(str1)) {
                    String str2rvs = reverseString(str2);
                    // str1 is palindrome, str2rvs + str1 + str2 = str2rvs + word is palindrome.
                    // 题目要求是存[i, j] which words[i] + words[j] is palindrome;
                    // 那么就是 [str2rvs index, i]
                    if(hashmap.containsKey(str2rvs) && hashmap.get(str2rvs) != i) {
                        List<Integer> list = new ArrayList<Integer>();
                        list.add(hashmap.get(str2rvs));
                        list.add(i);
                        lists.add(list);
                    }
                }
                // 注意str2不能为空,因为前面已经有str2为空的情况,已经计算过了,否则这里会有重复结果;
                // 另外,list add有顺序问题;
                // str2 is palindrom, str1 + str2 + str1rvs = word + str1rvs is palindrome
                // 题目要求是存[i, j] which words[i] + words[j] is palindrome;
                // 那么就是[i, str1rvs index];
                if(str2.length()!= 0 && isPalindrome(str2)) {
                    String str1rvs = reverseString(str1);
                    if(hashmap.containsKey(str1rvs) && hashmap.get(str1rvs) != i) {
                        List<Integer> list = new ArrayList<Integer>();
                        list.add(i);
                        list.add(hashmap.get(str1rvs));
                        lists.add(list);
                    }
                }
            }
        }
        return lists;
    }
    
    private boolean isPalindrome(String s) {
        int start = 0; int end = s.length() - 1;
        while(start <= end) {
            if(s.charAt(start) != s.charAt(end)) {
                return false;
            }
            start++;
            end--;
        }
        return true;
    }
    
    private String reverseString(String s) {
        char[] chars = s.toCharArray();
        int i = 0; int j = chars.length - 1;
        while(i <= j) {
            char temp = chars[i];
            chars[i] = chars[j];
            chars[j] = temp;
            i++;
            j--;
        }
        return new String(chars);
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值