Trie 类型的题目总结

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

Implement Trie (Prefix Tree)

实现一个 Trie,包含 insertsearch, 和 startsWith 这三个方法。

思路:模板必须记住;没有儿子建立儿子,有儿子走儿子;

public class Trie {
    
    private class TrieNode {
        TrieNode[] children;
        boolean isword;
        String word;
        
        public TrieNode () {
            this.children = new TrieNode[26];
            for(int i = 0; i < 26; i++){
                children[i] = null;
            }
            this.isword = false;
            this.word = null;
        }
    }
    
    private TrieNode root;
    public Trie() {
        root = new TrieNode();
    }

    /*
     * @param word: a word
     * @return: nothing
     */
    public void insert(String word) {
        if(word == null) {
            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;
        cur.word = word;
    }

    /*
     * @param word: A string
     * @return: if the word is in the trie.
     */
    public boolean search(String word) {
        if(word == null) {
            return false;
        }
        TrieNode cur = root;
        for(int i = 0; i < word.length(); i++) {
            char c = word.charAt(i);
            if(cur.children[c - 'a'] == null){
                return false;
            }
            cur = cur.children[c - 'a'];
        }
        return cur.isword && cur.word.equals(word);
    }

    /*
     * @param prefix: A string
     * @return: if there is any word in the trie that starts with the given prefix.
     */
    public boolean startsWith(String prefix) {
        if(prefix == null) {
            return false;
        }
        TrieNode cur = root;
        for(int i = 0; i < prefix.length(); i++) {
            char c = prefix.charAt(i);
            if(cur.children[c - 'a'] == null){
                return false;
            }
            cur = cur.children[c - 'a'];
        }
        return cur != root;
    }
}

Add and Search Word - Data structure design

设计一个包含下面两个操作的数据结构:addWord(word)search(word)

addWord(word)会在数据结构中添加一个单词。而search(word)则支持普通的单词查询或是只包含.a-z的简易正则表达式的查询。

一个 . 可以代表一个任何的字母。

Example

样例 1:

输入:
  addWord("a")
  search(".")
输出: 
  true

样例 2:

输入: 
  addWord("bad")
  addWord("dad")
  addWord("mad")
  search("pad")  
  search("bad")  
  search(".ad")  
  search("b..") 
输出:
  false
  true
  true
  true 

Notice

你可以认为所有的单词都只包含小写字母 a-z。

思路:遇见 ‘.’ 之后,for循环check每一个可能性;注意这题我写了两个坑:

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

2. for循环的时候,一定要判断cur.children[i] != null, 也就是判断单词是否插入到这条路径上了,否则就是null;不需要继续搜索;

public class WordDictionary {
    /*
     * @param word: Adds a word into the data structure.
     * @return: nothing
     */
    private class TrieNode {
        private TrieNode[] children;
        private boolean isword;
        private String word;
        
        public TrieNode () {
            children = new TrieNode[26];
            for(int i = 0; i < 26; i++){
                children[i] = null;
            }
            this.isword = false;
            this.word = null;
        }
    }
     
    private TrieNode root;
    public WordDictionary() {
        root = new TrieNode();
    }
    public void addWord(String word) {
        if(word == null) {
            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;
        cur.word = word;
    }

    /*
     * @param word: A word could contain the dot character '.' to represent any one letter.
     * @return: if the word is in the data structure.
     */
    public boolean search(String word) {
        if(word == null) {
            return false;
        }
        return dfs(word, 0,  root);
    }
    
    private boolean dfs(String word, int index, TrieNode cur) {
        if(index == word.length()){
            return cur.isword; //两个坑,第一,这里返回最后一个node 是不是isword;
        }
    
        char c = word.charAt(index);
        if(c == '.') {
            for(int i = 0; i < 26; i++) {
                // 第二,这里需要判断cur.children[i]是否是null;
                // 为什么,是为了防止根本没有节点input到这个node下面,也就是dictionary里面根本没有;
                if(cur.children[i] != null) {  
                    if(dfs(word, index+1, cur.children[i])) {
                        return true;
                    }
                }
            }
            return false;
        } else {
            if(cur.children[c - 'a'] == null) {
                return false;
            } else {
                return dfs(word, index+1, cur.children[c - 'a']);
            }
        }
    }
}

trie的剪枝 

Word Search II

给出一个由小写字母组成的矩阵和一个字典。找出所有同时在字典和矩阵中出现的单词。一个单词可以从矩阵中的任意位置开始,可以向左/右/上/下四个相邻方向移动。一个字母在一个单词中只能被使用一次。且字典中不存在重复单词

Example

样例 1:

输入:["doaf","agai","dcan"],["dog","dad","dgdg","can","again"]
输出:["again","can","dad","dog"]
解释:
  d o a f
  a g a i
  d c a n
矩阵中查找,返回 ["again","can","dad","dog"]。

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

public class Solution {
    /**
     * @param board: A list of lists of character
     * @param words: A list of string
     * @return: A list of string
     */
    
    private class TrieNode {
        public TrieNode[] children;
        public boolean isword;
        public String word;
        
        public TrieNode() {
            children = new TrieNode[26];
            for(int i = 0; i < 26; i++) {
                children[i] = null;
            }
            this.isword = false;
            this.word = null;
        }
    }
    
    private class TrieTree {
        TrieNode root;
        
        public TrieTree() {
            root = new TrieNode();
        }
        
        public void insert(String word) {
            if(word == null) {
                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;
            cur.word = word;
        }
    }
     
    public List<String> wordSearchII(char[][] board, List<String> words) {
        List<String> result = new ArrayList<String>();
        if(board == null || board.length == 0 || board[0].length == 0 || words == null) {
            return result;
        }
        
        // build trie tree;
        TrieTree trieTree = new TrieTree();
        for(String word: words) {
            trieTree.insert(word);
        }
        
        int n = board.length;
        int m = board[0].length;
        boolean[][] visited = new boolean[n][m];
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                // trieTree 相当于把 words全部传进去了;
                search(board, i, j, trieTree.root, result, visited);
            }
        }
        
        return result;
    }
    
    int[] dx = {0, 0, 1, -1};
    int[] dy = {1, -1, 0, 0};
    
    private void search(char[][] board, int x, int y, TrieNode cur, 
                        List<String> result, boolean[][] visited) {
        if(!isvalid(board, x, y, visited)) {
            return;
        }
        
        char c = board[x][y];
        TrieNode child = cur.children[c-'a'];
        if(child == null) {
            return;
        }
        
        // 注意这里是判断child的word是否是word,因为走了一步了;
        if(child.isword && child.word != null) {
            if(!result.contains(child.word)) {
                result.add(child.word); 
                // 而且这里搜集了一个单词之后,不需要return,继续搜下面的,下面也许还有单词;
            }
        }
        
        visited[x][y] = true;
        for(int k = 0; k < 4; k++) {
            int nx = x + dx[k];
            int ny = y + dy[k];
            if(isvalid(board, nx, ny, visited)) {
                search(board, nx, ny, child, result, visited);
            }
        }
        visited[x][y] = false;
    }
    
    private boolean isvalid(char[][] board, int nx, int ny, boolean[][] visited) {
        return (0 <= nx && nx < board.length && 0 <= ny && ny < board[0].length && !visited[nx][ny]);
    }
}

Word Squares

给出一系列 不重复的单词,找出所有用这些单词能构成的 单词矩阵
一个有效的单词矩阵是指, 如果从第 k 行读出来的单词和第 k 列读出来的单词相同(0 <= k < max(numRows, numColumns)),那么就是一个单词矩阵.
例如,单词序列为 ["ball","area","lead","lady"] ,构成一个单词矩阵。因为对于每一行和每一列,读出来的单词都是相同的。

b a l l
a r e a
l e a d
l a d y

Example

样例 1:

输入:
["area","lead","wall","lady","ball"]
输出:
[["wall","area","lead","lady"],["ball","area","lead","lady"]]

解释:
输出包含 两个单词矩阵,这两个矩阵的输出的顺序没有影响(只要求矩阵内部有序)。

思路:如何利用trie剪枝就是通过已经加入的单词list,构造出后面即将加入单词的prefix list,然后从prefix list中选取candidate;

public class Solution {
    /*
     * @param words: a set of words without duplicates
     * @return: all word squares
     */
    private class TrieNode {
        public TrieNode[] children;
        public List<String> prefixList;
        public boolean isword;
        public String word;
        
        public TrieNode () {
            children = new TrieNode[26];
            prefixList = new ArrayList<String>();
            isword = false;
            word = null;
        }
    }
    
    private class TrieTree {
        private TrieNode root;
        
        public TrieTree() {
            root = new TrieNode();
        }
        
        public void insert(String word) {
            if(word == null) {
                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.prefixList.add(word);
            }
            cur.isword = true;
            cur.word = word;
        }
        
        public List<String> getPrefixList(String prefix) {
            List<String> res = new ArrayList<String>();
            if(prefix == null) {
                return res;
            }
            TrieNode cur = root;
            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.prefixList);
            return res;
        }
    }
     
    public List<List<String>> wordSquares(String[] words) {
        List<List<String>> lists = new ArrayList<List<String>>();
        if(words == null || words.length == 0) {
            return lists;
        }
        
        TrieTree trieTree = new TrieTree();
        for(String word: words) {
            trieTree.insert(word);
        }
        
        int n = words[0].length();
        List<String>  list = new ArrayList<String>();
        for(String word: words) {
            list.add(word);
            dfs(trieTree, n, word, lists, list);
            list.remove(list.size() -1);
        }
        return lists;
    }
    
    private void dfs(TrieTree trieTree, int n,  String word, 
    List<List<String>> lists, List<String> list) {
        if(list.size() == n) {
            lists.add(new ArrayList<String>(list));
            return;
        }
        
        // 通过已经加入的单词,组成后面单词的prefix,来剪枝的;
        // 这样就规范了矩阵的顺序,能够排列成square;
        StringBuilder sb = new StringBuilder();
        int idx = list.size();
        for(int i = 0; i < list.size(); i++) {
            sb.append(list.get(i).charAt(idx));
        }
        String prefix = sb.toString();
       
        for(String str: trieTree.getPrefixList(prefix)){
            list.add(str);
            dfs(trieTree, n, str, lists, list);
            list.remove(list.size() - 1);
        }
    }
}
发布了562 篇原创文章 · 获赞 13 · 访问量 17万+

猜你喜欢

转载自blog.csdn.net/u013325815/article/details/103925998