Leetcode: Add and Search Word - Data structure design

时间:2023-01-21 16:55:09
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");