Leetcode - Trie Medium

LC421, LC208, LC211

Posted by freeCookie🍪 on December 27, 2016

Trie, 前缀树。个人习惯每个Node具有如下结构:

class Node{
  String word;
  Node[] next;
  public Node(){
    word = null;
    next = new Node[26];
  }
}

前缀树对于String的一些问题可以有效的减少时间复杂度。

LC208. Implement Trie

Implement a trie with insertsearch, and startsWith methods.

前缀树入门题,抄起Trie写。

class TrieNode {
    // Initialize your data structure here.
    String word;
    TrieNode[] next;
    public TrieNode() {
        word = null;
        next = new TrieNode[26];
    }
}

public class Trie {
    private TrieNode root;

    public Trie() {
        root = new TrieNode();
    }

    // Inserts a word into the trie.
    public void insert(String word) {
        TrieNode r = root;
        for(char c: word.toCharArray()){
            if(r.next[c-'a'] == null){
                r.next[c-'a'] = new TrieNode();
            }
            r = r.next[c-'a'];
        }
        r.word = word;
    }

    // Returns if the word is in the trie.
    public boolean search(String word) {
        TrieNode r = root;
        for(char c: word.toCharArray()){
            if(r.next[c-'a'] == null){
                return false;
            }
            r = r.next[c-'a'];
        }
        return r.word != null;
    }

    // Returns if there is any word in the trie
    // that starts with the given prefix.
    public boolean startsWith(String prefix) {
        TrieNode r = root;
        for(char c: prefix.toCharArray()){
            if(r.next[c-'a'] == null)   return false;
            r = r.next[c-'a'];
        }
        return true;
    }
}

// Your Trie object will be instantiated and called as such:
// Trie trie = new Trie();
// trie.insert("somestring");
// trie.search("key");

前缀树的实现最近写了几个题就比较熟悉了。

LC421. Maximum XOR of Two Numbers in an Array

Given a non-empty array of numbers, a0, a1, a2, … , an-1, where 0 ≤ ai < 2^31. Find the maximum result of ai XOR aj, where 0 ≤ ij < n. In O(n) runtime.

Analysis:

Discuss - bit manipulation

要求O(n), naive的做法是O(n^2). ai < 2^31,一定在暗示位运算。

  1. Bit manipulation

    想法是算出每一位的结果最后得到最终的结果,O(32n). 对于i属于 [0, 32)从左到右的每一位,首先找到每个num的i个前缀bit,首先假设max = 0,对于所有的前缀求出最大的可能性并保存到max中不断更新。

    public class Solution {
        public int findMaximumXOR(int[] nums) {
            int max = 0, mask = 0;
            for(int i = 31; i >= 0; i--){
                mask |= (1<<i);
                HashSet<Integer> set = new HashSet<Integer>();
                for(int num: nums){
                    set.add(num&mask);
                }
                int temp = max | (1<<i);
                for(int prefix:set){
                    if(set.contains(temp ^ prefix)){
                        max = temp;
                    }
                }
            }
            return max;
        }
    }
    
  2. Trie

    说好的前缀树tag呢 -> 将每个num加入二进制形式的trie,寻找最大的异或值,即对于每个num的反码是否存在。这个写法由于是O(60n)而139ms。还是discuss厉害。

Discuss: Trie, O(30n)

public class Solution {
    class Node{
        Node[] next;
        Integer num;
        public Node nextNode(int bit){
            if(next[bit] == null)   next[bit] = new Node();
            return next[bit];
        }
        public Node(){
            next = new Node[2];
            num = null;
        }
    }
    
    public int findMaximumXOR(int[] nums) {
        Node root = new Node();
        int max = 0;
        // build trie
        for(int num: nums){
            Node node = root;
            for(int i = 30; i >= 0; i--){
                int temp = (num >> i) & 1;
                if(node.next[temp] == null) node.next[temp] = new Node();
                node = node.next[temp];
            }
            node.num = num;
        }
        // find
        for(int num: nums){
            Node node = root;
            int maxtemp = 0;
            for(int i = 30; i >= 0; i--){
                int temp = (num >> i) & 1;
                if(node.next[1 ^ temp] != null){
                    node = node.next[1 ^ temp];
                    maxtemp |= (1 << i);
                }else{
                    node = node.next[temp];
                }
            } // for i
            max = Math.max(max, maxtemp);
        } // for num
        return max;
    }
}

LC211. Add and Search Word - Data structure design

Design a data structure that supports the following two operations:

void addWord(word)
bool search(word)

search(word) can search a literal word or a regular expression string containing only letters a-z or .. A . means it can represent any one letter.

Analysis:

前缀树 + 正则,前缀树的实现没有区别,搜索的时候区分char情况即可。

public class WordDictionary {
    // typical Trie 
    class Node{
        String word;
        Node[] next;
        public Node(){
            word = null;
            next = new Node[26];
        }
    }
    Node root = new Node();
    // Adds a word into the data structure.
    public void addWord(String word) {
        if(word == null)    return;
        Node node = root;
        char[] arr = word.toCharArray();
        for(char c: arr){
            if(node.next[c-'a'] == null){
                node.next[c-'a'] = new Node();
            }
            node = node.next[c-'a'];
        } // for
        node.word = 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 helper(word, root, 0);
    }
    
    // backtrack searching word
    public boolean helper(String word, Node node, int pos){
        if(node == null || word == null)    return false;
        char[] arr = word.toCharArray();
        if(pos == arr.length){
            if(node.word == null)   return false;
            //System.out.println(word);
            //System.out.println(node.word);
            //return node.word.matches(word);
            return true;
        }
        if(arr[pos] != '.'){
            return helper(word, node.next[arr[pos]-'a'], pos + 1);
        }else{
            for(int i = 0; i < 26; i++){
                if(helper(word, node.next[i], pos+1)){
                    return true;
                }
            }
        }
        return false;
    } // helper
}

// Your WordDictionary object will be instantiated and called as such:
// WordDictionary wordDictionary = new WordDictionary();
// wordDictionary.addWord("word");
// wordDictionary.search("pattern");

用递归的写法,最后不需要match(加了结果搞到0.99%)一定结果是正确的,遇到”.”直接匹配下一个字符即可。