@@ -60,22 +60,246 @@ streamChecker.query("l"); // 返回 True ,因为 'kl' 在 words 中
60
60
61
61
<!-- 这里可写通用的实现逻辑 -->
62
62
63
+ ** 方法一:前缀树**
64
+
65
+ 构建前缀树,每个节点保存一个字符,从根节点开始,每次遍历到一个字符,就将其加入到当前节点的子节点中,同时将当前节点的 ` isEnd ` 标记为 ` true ` 。当遍历到字符串的末尾时,将当前节点的 ` isEnd ` 标记为 ` true ` 。
66
+
67
+ 查询时,从根节点开始,每次遍历到一个字符,就将其加入到当前节点的子节点中,同时将当前节点的 ` isEnd ` 标记为 ` true ` 。当遍历到字符串的末尾时,将当前节点的 ` isEnd ` 标记为 ` true ` 。
68
+
69
+ 对于本题,我们采用** 字符串的反序** 来构建前缀树,这样在查询时,只需要从根节点开始,遍历到当前字符即可,不需要遍历到字符串的末尾。
70
+
71
+ 初始化前缀树的时间复杂度为 $O(n)$,其中 $n$ 为 ` words ` 所有字符总数。单次查询的时间复杂度为 $O(m)$,其中 $m$ 为 ` words ` 中单词的最大长度。
72
+
63
73
<!-- tabs:start -->
64
74
65
75
### ** Python3**
66
76
67
77
<!-- 这里可写当前语言的特殊实现逻辑 -->
68
78
69
79
``` python
80
+ class Trie :
81
+ def __init__ (self ):
82
+ self .children = [None ] * 26
83
+ self .is_end = False
84
+
85
+ def insert (self , w ):
86
+ node = self
87
+ for c in w[::- 1 ]:
88
+ idx = ord (c) - ord (' a' )
89
+ if node.children[idx] is None :
90
+ node.children[idx] = Trie()
91
+ node = node.children[idx]
92
+ node.is_end = True
93
+
94
+ def search (self , w ):
95
+ node = self
96
+ for c in w[::- 1 ]:
97
+ idx = ord (c) - ord (' a' )
98
+ if node.children[idx] is None :
99
+ return False
100
+ node = node.children[idx]
101
+ if node.is_end:
102
+ return True
103
+ return False
104
+
105
+
106
+ class StreamChecker :
70
107
108
+ def __init__ (self , words : List[str ]):
109
+ self .trie = Trie()
110
+ self .s = []
111
+ for w in words:
112
+ self .trie.insert(w)
113
+
114
+ def query (self , letter : str ) -> bool :
115
+ self .s.append(letter)
116
+ return self .trie.search(self .s[- 201 :])
117
+
118
+ # Your StreamChecker object will be instantiated and called as such:
119
+ # obj = StreamChecker(words)
120
+ # param_1 = obj.query(letter)
71
121
```
72
122
73
123
### ** Java**
74
124
75
125
<!-- 这里可写当前语言的特殊实现逻辑 -->
76
126
77
127
``` java
128
+ class Trie {
129
+ Trie [] children = new Trie [26 ];
130
+ boolean isEnd = false ;
131
+
132
+ public void insert (String w ) {
133
+ Trie node = this ;
134
+ for (int i = w. length() - 1 ; i >= 0 ; -- i) {
135
+ int idx = w. charAt(i) - ' a' ;
136
+ if (node. children[idx] == null ) {
137
+ node. children[idx] = new Trie ();
138
+ }
139
+ node = node. children[idx];
140
+ }
141
+ node. isEnd = true ;
142
+ }
143
+
144
+ public boolean query (StringBuilder s ) {
145
+ Trie node = this ;
146
+ for (int i = s. length() - 1 , j = 0 ; i >= 0 && j < 201 ; -- i, ++ j) {
147
+ int idx = s. charAt(i) - ' a' ;
148
+ if (node. children[idx] == null ) {
149
+ return false ;
150
+ }
151
+ node = node. children[idx];
152
+ if (node. isEnd) {
153
+ return true ;
154
+ }
155
+ }
156
+ return false ;
157
+ }
158
+ }
159
+
160
+ class StreamChecker {
161
+ private StringBuilder sb = new StringBuilder ();
162
+ private Trie trie = new Trie ();
163
+
164
+ public StreamChecker (String [] words ) {
165
+ for (String w : words) {
166
+ trie. insert(w);
167
+ }
168
+ }
169
+
170
+ public boolean query (char letter ) {
171
+ sb. append(letter);
172
+ return trie. query(sb);
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Your StreamChecker object will be instantiated and called as such:
178
+ * StreamChecker obj = new StreamChecker(words);
179
+ * boolean param_1 = obj.query(letter);
180
+ */
181
+ ```
182
+
183
+ ### ** C++**
184
+
185
+ ``` cpp
186
+ class Trie {
187
+ public:
188
+ vector<Trie* > children;
189
+ bool isEnd;
190
+
191
+ Trie()
192
+ : children(26)
193
+ , isEnd(false) { }
194
+
195
+ void insert (string& w) {
196
+ Trie* node = this;
197
+ reverse(w.begin(), w.end());
198
+ for (char c : w) {
199
+ int idx = c - 'a';
200
+ if (!node->children[ idx] ) node->children[ idx] = new Trie();
201
+ node = node->children[ idx] ;
202
+ }
203
+ node->isEnd = true;
204
+ }
205
+
206
+ bool search(string& w) {
207
+ Trie* node = this;
208
+ for (int i = w.size() - 1, j = 0; ~i && j < 201; --i, ++j) {
209
+ int idx = w[i] - 'a';
210
+ if (!node->children[idx]) return false;
211
+ node = node->children[idx];
212
+ if (node->isEnd) return true;
213
+ }
214
+ return false;
215
+ }
216
+ };
217
+
218
+ class StreamChecker {
219
+ public:
220
+ Trie* trie = new Trie();
221
+ string s;
222
+ StreamChecker(vector<string >& words) {
223
+ for (string& w : words) {
224
+ trie->insert(w);
225
+ }
226
+ }
227
+
228
+ bool query(char letter) {
229
+ s += letter;
230
+ return trie->search(s);
231
+ }
232
+ };
233
+
234
+ /**
235
+ * Your StreamChecker object will be instantiated and called as such:
236
+ * StreamChecker* obj = new StreamChecker(words);
237
+ * bool param_1 = obj->query(letter);
238
+ * /
239
+ ```
240
+
241
+ ### **Go**
242
+
243
+ ```go
244
+ type Trie struct {
245
+ children [26]*Trie
246
+ isEnd bool
247
+ }
248
+
249
+ func newTrie() Trie {
250
+ return Trie{}
251
+ }
252
+
253
+ func (this *Trie) Insert(word string) {
254
+ node := this
255
+ for i := len(word) - 1; i >= 0; i-- {
256
+ idx := word[i] - 'a'
257
+ if node.children[idx] == nil {
258
+ node.children[idx] = &Trie{}
259
+ }
260
+ node = node.children[idx]
261
+ }
262
+ node.isEnd = true
263
+ }
264
+
265
+ func (this *Trie) Search(word []byte) bool {
266
+ node := this
267
+ for i, j := len(word)-1, 0; i >= 0 && j < 201; i, j = i-1, j+1 {
268
+ idx := word[i] - 'a'
269
+ if node.children[idx] == nil {
270
+ return false
271
+ }
272
+ node = node.children[idx]
273
+ if node.isEnd {
274
+ return true
275
+ }
276
+ }
277
+ return false
278
+ }
279
+
280
+ type StreamChecker struct {
281
+ trie Trie
282
+ s []byte
283
+ }
284
+
285
+ func Constructor(words []string) StreamChecker {
286
+ trie := newTrie()
287
+ for _, w := range words {
288
+ trie.Insert(w)
289
+ }
290
+ return StreamChecker{trie, []byte{}}
291
+ }
292
+
293
+ func (this *StreamChecker) Query(letter byte) bool {
294
+ this.s = append(this.s, letter)
295
+ return this.trie.Search(this.s)
296
+ }
78
297
298
+ /**
299
+ * Your StreamChecker object will be instantiated and called as such:
300
+ * obj := Constructor(words);
301
+ * param_1 := obj.Query(letter);
302
+ */
79
303
```
80
304
81
305
### ** ...**
0 commit comments