Lesson 112 of 250
211. Design Add and Search Words Data Structure
NeetCode: Design Add and Search Words Data Structure · LeetCode: 211. Design Add and Search Words Data Structure · Difficulty: Medium
Design a WordDictionary with addWord(word) and search(word). The stored words use lowercase letters. The searched word may contain ., which matches any single letter.
Example
addWord("day"); addWord("bay"); addWord("may")
search("say") -> false
search("day") -> true
search(".ay") -> true
search("b..") -> trueBrute force: test the pattern against every word
Keep every word in a List<String>. A word matches the pattern when the lengths are equal and every position is either . or the same character.
import java.util.ArrayList;
import java.util.List;
class WordDictionary {
private final List<String> words = new ArrayList<>();
public WordDictionary() {}
public void addWord(String word) {
words.add(word);
}
public boolean search(String pattern) {
for (String w : words) {
if (matches(w, pattern)) return true;
}
return false;
}
private boolean matches(String word, String pattern) {
if (word.length() != pattern.length()) return false;
for (int i = 0; i < word.length(); i++) {
char p = pattern.charAt(i);
if (p != '.' && p != word.charAt(i)) return false;
}
return true;
}
}
public class Main {
public static void main(String[] args) {
WordDictionary dict = new WordDictionary();
for (String w : new String[] {"day", "bay", "may"}) {
dict.addWord(w);
}
System.out.println(dict.search("say"));
System.out.println(dict.search("day"));
System.out.println(dict.search(".ay"));
System.out.println(dict.search("b.."));
}
}false
true
true
trueTime: O(N · L) per search. Space: O(N · L).
Optimal: trie with DFS on wildcards
Store the words in a trie: each node holds an array of 26 child references (null when absent) and an end-of-word flag, and the child for letter c is at index c - 'a'. For a normal letter, follow that one child. For ., try every existing child and succeed if any branch matches the rest of the pattern. Without wildcards a search is O(L); each . multiplies the work by the branching factor, limited to nodes that exist.
class TrieNode {
TrieNode[] children = new TrieNode[26];
boolean isEnd;
}
class WordDictionary {
private final TrieNode root = new TrieNode();
public WordDictionary() {}
public void addWord(String word) {
TrieNode node = root;
for (int i = 0; i < word.length(); i++) {
int c = word.charAt(i) - 'a';
if (node.children[c] == null) node.children[c] = new TrieNode();
node = node.children[c];
}
node.isEnd = true;
}
public boolean search(String pattern) {
return matches(root, pattern, 0);
}
// Does the pattern from index i onward match some path below node?
private boolean matches(TrieNode node, String pattern, int i) {
if (i == pattern.length()) return node.isEnd;
char ch = pattern.charAt(i);
if (ch == '.') {
for (TrieNode child : node.children) {
if (child != null && matches(child, pattern, i + 1)) return true;
}
return false;
}
TrieNode child = node.children[ch - 'a'];
return child != null && matches(child, pattern, i + 1);
}
}
public class Main {
public static void main(String[] args) {
WordDictionary dict = new WordDictionary();
for (String w : new String[] {"day", "bay", "may"}) {
dict.addWord(w);
}
System.out.println(dict.search("say"));
System.out.println(dict.search("day"));
System.out.println(dict.search(".ay"));
System.out.println(dict.search("b.."));
System.out.println(dict.search("..."));
System.out.println(dict.search("...."));
}
}false
true
true
true
true
falseTime: addWord is O(L). search is O(L) for exact patterns and up to O(26^d · L) worst case for d wildcards. Space: O(total letters).
