原文链接
https://blog.baozitraining.org/2019/03/leetcode-solution-211-add-and-search.html
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.
Example:
addWord("bad")
addWord("dad")
addWord("mad")
search("pad") -> false
search("bad") -> true
search(".ad") -> true
search("b..") -> true
Problem link
You can find the detailed video tutorial here
https://youtu.be/qi2ohSEyyDw
It's a string pattern searching problem, and because it matches the word strictly from the beginning to the end (note, even the partial matching is considered False, e.g., word "abcd", search("abc") should return False), the Trie (prefix tree) data structure comes very handy. It's a common simple data structure that often comes in coding interviews, be sure you can write it bug free. Once we are familiar with Trie, addWord simply builds the Trie and search just looks for the exact matching. One tricky part of this problem is ".", which can match any one and only one letter. This requires a recursive search of all the sub-nodes in the trie when encountering a "." Even not asked in this leetcode question, but there are some very good follow up question
class TrieNode {
public Character letter = null;
// I just like to indicate whether a node is end or not, being explicit and clear, also it's good to know
// if a word is actually a word or a substring of a long word
public boolean isEnd = false;
// This can be optimized by a 256 char array.
public Map<Character, TrieNode> children = new HashMap<>();
}
public class AddAndSearchWord {
private TrieNode root;
public AddAndSearchWord() {
this.root = new TrieNode();
}
// Adds a word into the data structure.
public void addWord(String word) {
if (word == null || word.length() == 0) {
return;
}
this.insertHelper(this.root, word, 0);
}
// this can be done iteratively too
private void insertHelper(TrieNode node, String word, int index) {
if (index == word.length()) {
node.isEnd = true;
return;
}
char c = word.charAt(index);
if (node.children.containsKey(c)) {
insertHelper(node.children.get(c), word, index + 1);
return;
}
TrieNode t = new TrieNode();
t.letter = c;
node.children.put(c, t);
insertHelper(t, word, index + 1);
}
// Returns if the word is in the trie. A word could
// contain the dot character '.' to represent any one and only one letter.
public boolean search(String word) {
if (word == null) {
return false;
}
return this.searchHelper(this.root, word, 0);
}
// DFS to search the word, worst case is to search the entire trie space if all dots
private boolean searchHelper(TrieNode node, String word, int index) {
if (node == null) {
return false;
}
// This is strict word matching, not including the substring, e.g., add("abcde"), search("abc") false, seasrch("abcde") true
if (index == word.length()) {
return node.isEnd;
}
char c = word.charAt(index);
if (c != '.') {
if (!node.children.containsKey(c)) {
return false;
}
return searchHelper(node.children.get(c), word, index + 1);
} else {
for (Map.Entry<Character, TrieNode> entry : node.children.entrySet()) {
// Don't need to go through a to z, just go through neighbours is enough, DFS
if (searchHelper(entry.getValue(), word, index + 1)) {
return true;
}
}
return false;
}
}
}
Time Complexity:
Space Complexity: