Leetcode Study Day 47

Implement Trie (Prefix Tree)

A trie (pronounced as “try”) or prefix tree is a tree data structure used to efficiently store and retrieve keys in a dataset of strings. There are various applications of this data structure, such as autocomplete and spellchecker.

Implement the Trie class:

Trie() Initializes the trie object.
void insert(String word) Inserts the string word into the trie.
boolean search(String word) Returns true if the string word is in the trie (i.e., was inserted before), and false otherwise.
boolean startsWith(String prefix) Returns true if there is a previously inserted string word that has the prefix prefix, and false otherwise.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Example 1:

Input
["Trie", "insert", "search", "search", "startsWith", "insert", "search"]
[[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
Output
[null, null, true, false, true, null, true]

Explanation
Trie trie = new Trie();
trie.insert("apple");
trie.search("apple"); // return True
trie.search("app"); // return False
trie.startsWith("app"); // return True
trie.insert("app");
trie.search("app"); // return True


Constraints:

1 <= word.length, prefix.length <= 2000
word and prefix consist only of lowercase English letters.
At most 3 * 104 calls in total will be made to insert, search, and startsWith.

My solution

My solution is easy to understand but cost a lot of time to process.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Trie {
public:
Trie() {

}

void insert(string word) {
wordSet.insert(word);
}

bool search(string word) {
if (wordSet.find(word) != wordSet.end())
return true;
else return false;
}

bool startsWith(string prefix) {
int n = prefix.size();
for(string word: wordSet){
for(int i = 0; i < n; i++){
if(prefix[i] != word[i])
break;
if(prefix[i] == word[i] && i == n - 1)
return true;
}
}
return false;
}

private:
unordered_set <string> wordSet;

};

/**
* Your Trie object will be instantiated and called as such:
* Trie* obj = new Trie();
* obj->insert(word);
* bool param_2 = obj->search(word);
* bool param_3 = obj->startsWith(prefix);
*/

Prefix Tree

I got the idea of using prefix tree from this post in Leetcode.

I am going to explain his code with comment:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
class TrieNode {
public:
// create an array with 26 TrieNode pointers
TrieNode* children[26];
bool isWordCompleted;

TrieNode() {
memset(children, 0, sizeof(children));
isWordCompleted = false;
}
};

class Trie {
public:
TrieNode *root;
Trie() {
root = new TrieNode();
}

void insert(string word) {
TrieNode* newRoot = root;
for (char ch : word) {
// get the index of the alphabet
int alphabetIndex = ch - 'a';
// if the pointer is NULL, create a new TrieNode
if (newRoot -> children[alphabetIndex] == NULL) {
newRoot -> children[alphabetIndex] = new TrieNode();
}
// move the pointer to the next TrieNode
newRoot = newRoot -> children[alphabetIndex];
}
// mark the word as completed
newRoot -> isWordCompleted = true;
}

bool search(string word) {
TrieNode* newRoot = root;
for (char ch : word) {
int alphabetIndex = ch - 'a';
// if the pointer is NULL, means the character is not in the tree
if (newRoot -> children[alphabetIndex] == NULL) {
return false;
}
newRoot = newRoot -> children[alphabetIndex];
}
// if the word is completed, return true
if (newRoot -> isWordCompleted == true) {
return true;
}
return false;
}

bool startsWith(string prefix) {
TrieNode* newRoot = root;
for (char ch : prefix) {
int alphabetIndex = ch - 'a';
if (newRoot -> children[alphabetIndex] == NULL) {
return false;
}
newRoot = newRoot -> children[alphabetIndex];
}
return true;
}
};

/**
* Your Trie object will be instantiated and called as such:
* Trie* obj = new Trie();
* obj->insert(word);
* bool param_2 = obj->search(word);
* bool param_3 = obj->startsWith(prefix);
*/

Design Add and Search Words Data Structure

Design a data structure that supports adding new words and finding if a string matches any previously added string.

Implement the WordDictionary class:

WordDictionary() Initializes the object.
void addWord(word) Adds word to the data structure, it can be matched later.
bool search(word) Returns true if there is any string in the data structure that matches word or false otherwise. word may contain dots ‘.’ where dots can be matched with any letter.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Example:

Input
["WordDictionary","addWord","addWord","addWord","search","search","search","search"]
[[],["bad"],["dad"],["mad"],["pad"],["bad"],[".ad"],["b.."]]
Output
[null,null,null,null,false,true,true,true]

Explanation
WordDictionary wordDictionary = new WordDictionary();
wordDictionary.addWord("bad");
wordDictionary.addWord("dad");
wordDictionary.addWord("mad");
wordDictionary.search("pad"); // return False
wordDictionary.search("bad"); // return True
wordDictionary.search(".ad"); // return True
wordDictionary.search("b.."); // return True


Constraints:

1 <= word.length <= 25
word in addWord consists of lowercase English letters.
word in search consist of '.' or lowercase English letters.
There will be at most 2 dots in word for search queries.
At most 104 calls will be made to addWord and search.

Solution

Based on the idea of previous problem, we can use prefix tree to solve this problem. The only difference is that we need to consider the ‘.’ in the word. We can use recursive method to search the word in the tree. If we meet ‘.’, we need to search all the children of the current node. If the node we find is not correct, we need to move to the next node. If we run out of all 26 alphabet, we return false.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
class TrieNode{
public:
TrieNode* children[26];
bool isCompleted;
TrieNode(){
memset(children, 0, sizeof(children));
isCompleted = false;
}
};

class WordDictionary {
public:
TrieNode *root ;
WordDictionary() {
root = new TrieNode();
}

void addWord(string word) {
TrieNode * newroot = root;
for(char character: word){
int alphabetIndex = character - 'a';
if(newroot->children[alphabetIndex] == NULL)
newroot->children[alphabetIndex] = new TrieNode();
newroot = newroot->children[alphabetIndex];
}
newroot -> isCompleted = true;
}

bool search(string word) {
return searchRecursive(word, 0, root);
}
private:
bool searchRecursive(const string & word, int index, TrieNode* node){
if (index == word.size()){
return node->isCompleted;
}
char ch = word[index];
if(ch != '.'){
int alphabetIndex = ch - 'a';
if(node->children[alphabetIndex] == NULL)
return false;
return searchRecursive(word, index + 1, node->children[alphabetIndex]);
}
if(ch == '.'){
for (int i = 0; i < 26; i++){
if(node->children[i] != NULL){
if (searchRecursive(word, index + 1, node->children[i]))
return true;
}
}
}
return false;
}
};

/**
* Your WordDictionary object will be instantiated and called as such:
* WordDictionary* obj = new WordDictionary();
* obj->addWord(word);
* bool param_2 = obj->search(word);
*/
  • Copyrights © 2020-2024 Yangyang Cui

请我喝杯咖啡吧~

支付宝
微信