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. For example: addWord("bad") addWord("dad") addWord("mad") search("pad") -> false search("bad") -> true search(".ad") -> true search("b..") -> true Note: You may assume that all words are consist of lowercase letters a-z.
方法1:brute force: search function compares each word with the pattern, time complexity: O(nk), n is number of words in dictionary, k is averge length
方法2: trie, search use recursion, time complexity: O(nk)? space O(nk)?
This is related to previous problem: Implement Trie
Only because in this problem, "." is introduced, which will cause us to backtrack to check each son. Using iteration is not that convient, we use recursion here.
One thing to be cautious is that when inserting words to a trie, the last node should have node.isEnd set to be true. Always forget this
1 public class WordDictionary { 2 public class TrieNode { 3 int num; //count how many words go through this TrieNode 4 TrieNode[] sons; //Arrays of children 5 boolean isEnd; 6 char val; 7 8 public TrieNode() { 9 this.num = 0; 10 this.sons = new TrieNode[26]; 11 this.isEnd = false; 12 } 13 } 14 15 TrieNode root; 16 17 public WordDictionary() { 18 this.root = new TrieNode(); 19 } 20 21 // Adds a word into the data structure. 22 public void addWord(String word) { 23 if (word==null || word.length()==0) return; 24 char[] arr = word.toCharArray(); 25 TrieNode node = this.root; 26 for (char c : arr) { 27 int pos = (int)(c - 'a'); 28 if (node.sons[pos] == null) { 29 node.sons[pos] = new TrieNode(); 30 node.sons[pos].num = 1; 31 node.sons[pos].val = c; 32 } 33 else { 34 node.sons[pos].num++; 35 } 36 node = node.sons[pos]; 37 } 38 node.isEnd = true; 39 } 40 41 // Returns if the word is in the data structure. A word could 42 // contain the dot character '.' to represent any one letter. 43 public boolean search(String word) { 44 int len = word.length(); 45 TrieNode node = this.root; 46 return search(word, node, 0, len); 47 } 48 49 public boolean search(String word, TrieNode node, int cur, int len) { 50 if (cur == len) return node.isEnd; 51 char w = word.charAt(cur); 52 if (w == '.') { 53 for (int j=0; j<26; j++) { 54 if (node.sons[j] != null) { 55 if (search(word, node.sons[j], cur+1, len)) 56 return true; 57 } 58 } 59 return false; 60 } 61 else { 62 int pos = (int)(w - 'a'); 63 if (node.sons[pos] == null) return false; 64 return search(word, node.sons[pos], cur+1, len); 65 } 66 } 67 } 68 69 // Your WordDictionary object will be instantiated and called as such: 70 // WordDictionary wordDictionary = new WordDictionary(); 71 // wordDictionary.addWord("word"); 72 // wordDictionary.search("pattern");
Follow Up: any other solution?
1. Preprocess the dictionary, store map<key, set<words>>, key is the pattern, like ".a.e.c", time complexity O(1), space complexity O(2^k), k is average word length
2. still use trie, but for each trieNode, introduce a child '.', in the meanwhile maintain a set of words matched by current prefix, time complexity O(k), space: each level's space will be twice the orignal trie's space of this level.
Follow Up: 如果现在word里面包含的不再是'.', 而是‘*’, 能match 0个到多个字符,问该怎么处理?
1 public boolean search(String word, TrieNode node, int cur, int len) { 2 if (cur == len) return node.isEnd; 3 char w = word.charAt(cur); 4 if (w == '*') { 5 if (search(word, node, cur+1, len)) return true; //'*' match zero char 6 for (int j=0; j<26; j++) { 7 if (node.sons[j] != null) { 8 if (search(word, node.sons[j], cur, len)) //skip one TrieNode, search next level, so '*' match 1 or more chars 9 return true; 10 } 11 } 12 return false; 13 } 14 else { 15 int pos = (int)(w - 'a'); 16 if (node.sons[pos] == null) return false; 17 return search(word, node.sons[pos], cur+1, len); 18 } 19 }
完整代码在这里:
1 package GooglePhone; 2 3 4 public class Solution { 5 public class TrieNode { 6 int num; //count how many words go through this TrieNode 7 TrieNode[] sons; //Arrays of children 8 boolean isEnd; 9 char val; 10 11 public TrieNode() { 12 this.num = 0; 13 this.sons = new TrieNode[26]; 14 this.isEnd = false; 15 } 16 } 17 18 TrieNode root; 19 20 public Solution() { 21 this.root = new TrieNode(); 22 } 23 24 // Adds a word into the data structure. 25 public void addWord(String word) { 26 if (word==null || word.length()==0) return; 27 char[] arr = word.toCharArray(); 28 TrieNode node = this.root; 29 for (char c : arr) { 30 int pos = (int)(c - 'a'); 31 if (node.sons[pos] == null) { 32 node.sons[pos] = new TrieNode(); 33 node.sons[pos].num = 1; 34 node.sons[pos].val = c; 35 } 36 else { 37 node.sons[pos].num++; 38 } 39 node = node.sons[pos]; 40 } 41 node.isEnd = true; 42 } 43 44 // Returns if the word is in the data structure. A word could 45 // contain the dot character '.' to represent any one letter. 46 public boolean search(String word) { 47 int len = word.length(); 48 TrieNode node = this.root; 49 return search(word, node, 0, len); 50 } 51 52 public boolean search(String word, TrieNode node, int cur, int len) { 53 if (cur == len) return node.isEnd; 54 char w = word.charAt(cur); 55 if (w == '*') { 56 if (search(word, node, cur+1, len)) return true; //'*' match zero char 57 for (int j=0; j<26; j++) { 58 if (node.sons[j] != null) { 59 if (search(word, node.sons[j], cur, len)) //skip one TrieNode, search next level, so '*' match 1 or more chars 60 return true; 61 } 62 } 63 return false; 64 } 65 else { 66 int pos = (int)(w - 'a'); 67 if (node.sons[pos] == null) return false; 68 return search(word, node.sons[pos], cur+1, len); 69 } 70 } 71 72 73 public static void main(String[] args) { 74 // TODO Auto-generated method stub 75 Solution sol = new Solution(); 76 sol.addWord("food"); 77 sol.addWord("hello"); 78 boolean res = sol.search("*f*d"); 79 if (res) System.out.println("true"); 80 else System.out.println("false"); 81 } 82 } 83 84 // Your WordDictionary object will be instantiated and called as such: 85 // WordDictionary wordDictionary = new WordDictionary(); 86 // wordDictionary.addWord("word"); 87 // wordDictionary.search("pattern");