Skip to content

Commit 4101a8f

Browse files
committed
LeetCode 211. Design Add and Search Words Data Structure Rust
1 parent 058d1d7 commit 4101a8f

File tree

3 files changed

+132
-4
lines changed

3 files changed

+132
-4
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ Solutions to LeetCode problems. The first column links to the problem in LeetCod
151151
| [207. Course Schedule][lc207] | 🟠 Medium | [![python](res/py.png)][lc207py] |
152152
| [208. Implement Trie (Prefix Tree)][lc208] | 🟠 Medium | [![python](res/py.png)][lc208py] [![rust](res/rs.png)][lc208rs] |
153153
| [210. Course Schedule II][lc210] | 🟠 Medium | [![python](res/py.png)][lc210py] |
154-
| [211. Design Add and Search Words Data Structure][lc211] | 🟠 Medium | [![python](res/py.png)][lc211py] |
154+
| [211. Design Add and Search Words Data Structure][lc211] | 🟠 Medium | [![python](res/py.png)][lc211py] [![rust](res/rs.png)][lc211rs] |
155155
| [212. Word Search II][lc212] | 🔴 Hard | [![python](res/py.png)][lc212py] |
156156
| [213. House Robber II][lc213] | 🟠 Medium | [![python](res/py.png)][lc213py] |
157157
| [215. Kth Largest Element in an Array][lc215] | 🟠 Medium | [![python](res/py.png)][lc215py] |
@@ -741,6 +741,7 @@ Solutions to LeetCode problems. The first column links to the problem in LeetCod
741741
[lc210py]: leetcode/course-schedule-ii.py
742742
[lc211]: https://leetcode.com/problems/design-add-and-search-words-data-structure/
743743
[lc211py]: leetcode/design-add-and-search-words-data-structure.py
744+
[lc211rs]: leetcode/design-add-and-search-words-data-structure.rs
744745
[lc212]: https://leetcode.com/problems/word-search-ii/
745746
[lc212py]: leetcode/word-search-ii.py
746747
[lc213]: https://leetcode.com/problems/house-robber-ii/

leetcode/design-add-and-search-words-data-structure.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
# inserted could make its own node, in average it will be less than that
2525
# because words with common roots will share nodes.
2626
#
27-
# Runtime: 16919 ms, faster than 27.72%
28-
# Memory Usage: 55.9 MB, less than 87.04%
27+
# Runtime: 9102 ms, faster than 74.25%
28+
# Memory Usage: 55.9 MB, less than 88.21%
2929
class WordDictionary:
3030
def __init__(self):
3131
self.root = {}
@@ -76,7 +76,7 @@ def __init__(self):
7676
# inserted could make its own node, in average it will be less than that
7777
# because words with common roots will share nodes.
7878
#
79-
# Runtime: 16930 ms, faster than 27.61%
79+
# Runtime: 10162 ms, faster than 60.45%
8080
# Memory Usage: 80 MB, less than 12.93%
8181
class WDEasyRead:
8282
def __init__(self):
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// 211. Design Add and Search Words Data Structure
2+
// 🟠 Medium
3+
//
4+
// https://leetcode.com/problems/design-add-and-search-words-data-structure/
5+
//
6+
// Tags: String - Depth-First Search - Design - Trie
7+
8+
use std::collections::HashMap;
9+
10+
#[derive(Default)]
11+
struct WordDictionary {
12+
children: HashMap<char, WordDictionary>,
13+
is_word_end: bool,
14+
}
15+
16+
/**
17+
* Implement the word dictionary using plain hash maps to store the
18+
* children of each node. Inserting is exactly the same as with the
19+
* regular trie class and can be done in O(n), searching will need to
20+
* branch out to all the nodes' children when the current character is a
21+
* '.' wildcard and it could potentially take O(m*n) which is visiting
22+
* each node in the trie, even though usually would be faster.
23+
*
24+
* Time complexity: O(m*n) - For searching strings with wildcards, it
25+
* could potentially search the entire trie. O(n) for inserts, it will
26+
* perform one O(1) operation per character.
27+
* Space complexity: O(m*n) - Potentially, each character of each node
28+
* inserted could make its own node, in average it will be less than that
29+
* because words with common roots will share nodes.
30+
*
31+
* Runtime 732 ms Beats 60.71%
32+
* Memory 45.9 Beats 32.14%
33+
*/
34+
impl WordDictionary {
35+
/**
36+
* Time complexity: O(1) - We create one empty HashMap instance.
37+
* Space complexity: O(1) - Constant space.
38+
*/
39+
fn new() -> Self {
40+
Self {
41+
is_word_end: false,
42+
children: HashMap::new(),
43+
}
44+
}
45+
46+
/**
47+
* Time complexity: O(1) - Word is limited to 25 chars, we may loop 25 times
48+
* and do O(1) work for each iteration.
49+
* Space complexity: O(1) - Constant space.
50+
*/
51+
fn add_word(&mut self, word: String) {
52+
let mut node = self;
53+
for ch in word.chars() {
54+
node = node.children.entry(ch).or_default();
55+
}
56+
node.is_word_end = true;
57+
}
58+
59+
/**
60+
* Time O(m*n) - Each wildcard will branch to all children, we may visit all
61+
* nodes in the word dictionary.
62+
* Space O(h) - The call stack can grow to the height of the tree, limited to
63+
* 20 so this is equivalent to O(1) in this case.
64+
*/
65+
fn search(&self, word: String) -> bool {
66+
let chars: Vec<char> = word.chars().collect();
67+
self.search_from(&self, 0, &chars)
68+
}
69+
70+
/**
71+
* Time O(m*n) - Each wildcard will branch to all children, we may visit all
72+
* nodes in the word dictionary.
73+
* Space O(h) - The call stack can grow to the height of the tree, limited to
74+
* 20 so this is equivalent to O(1) in this case.
75+
*/
76+
fn search_from(&self, current: &WordDictionary, idx: usize, chars: &Vec<char>) -> bool {
77+
let mut node = current;
78+
let mut ch;
79+
for i in idx..chars.len() {
80+
ch = chars[i];
81+
match ch {
82+
// When we find a wildcard, we need to try all children.
83+
'.' => {
84+
for (_, child) in node.children.iter() {
85+
// If any children is a match return true.
86+
if self.search_from(child, i + 1, chars) {
87+
return true;
88+
}
89+
}
90+
return false;
91+
}
92+
// If it is a regular character, match it.
93+
c => match node.children.get(&c) {
94+
Some(child) => node = child,
95+
None => return false,
96+
},
97+
}
98+
}
99+
// If we get to the end of the word.
100+
node.is_word_end
101+
}
102+
}
103+
104+
// Tests.
105+
fn main() {
106+
// Example test case.
107+
let mut wd = WordDictionary::new();
108+
wd.add_word(String::from("bad"));
109+
wd.add_word(String::from("dad"));
110+
wd.add_word(String::from("mad"));
111+
assert_eq!(wd.search(String::from("pad")), false);
112+
assert_eq!(wd.search(String::from("bad")), true);
113+
assert_eq!(wd.search(String::from(".ad")), true);
114+
assert_eq!(wd.search(String::from("b..")), true);
115+
116+
wd = WordDictionary::new();
117+
wd.add_word(String::from("a"));
118+
wd.add_word(String::from("a"));
119+
assert_eq!(wd.search(String::from(".")), true);
120+
assert_eq!(wd.search(String::from("a")), true);
121+
assert_eq!(wd.search(String::from("aa")), false);
122+
assert_eq!(wd.search(String::from("a")), true);
123+
assert_eq!(wd.search(String::from(".a")), false);
124+
assert_eq!(wd.search(String::from("a.")), false);
125+
126+
println!("All tests passed!")
127+
}

0 commit comments

Comments
 (0)