Problem
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)
Addsword
to the data structure, it can be matched later.bool search(word)
Returnstrue
if there is any string in the data structure that matchesword
orfalse
otherwise.word
may contain dots'.'
where dots can be matched with any letter.
Examples
Example 1:
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
Solution
Method 1 - Use Hashmap Based Trie
This problem is similar with Implement Trie Problem. The solution 1 below uses the same definition of a trie node. To handle the “.” case for this problem, we need to search all possible paths, instead of one path.
Here is TrieNode from Map-based Trie Implementation:
class TrieNode {
HashMap<Character, TrieNode> children = new HashMap<Character, TrieNode> ();
boolean isLeaf;
}
Here is WordDictionary:
public class WordDictionary {
private final TrieNode root = new TrieNode();
// Adds a word into the data structure.
public void addWord(String word) {
TrieNode curr = root;
for (int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
if (!curr.children.containsKey(c)) {
curr.children.put(c, new TrieNode());
}
curr = curr.children.get(c);
}
curr.isLeaf = true;
}
// Returns if the word is in the data structure. A word could
// contain the dot character '.' to represent any one letter.
public boolean search(String word) {
return dfsSearch(word, root);
}
private boolean dfsSearch(String word, TrieNode node) {
if (word == null || (word.isEmpty())) {
return node.isLeaf;
}
char c = word.charAt(0);
if (c == '.') {
for (TrieNode child : node.children.values()) {
if (dfsSearch(word.substring(1), child)) {
return true;
}
}
}
//if character is neither equal to the node nor '.', return false
if (!node.children.containsKey(c)) {
return false;
}
return dfsSearch(word.substring(1), node.children.get(c));
}
}
Our search
method is a bit heavy, as we are creating multiple substring of the word. here is a lighter method:
public boolean search(String word) {
return dfsSearch(word, 0, root);
}
private boolean dfsSearch(String word, int pos, TrieNode node) {
if (pos == word.length()) {
return node.isWord;
}
char c = word.charAt(pos);
if (c == '.') {
for (TrieNode child : node.children.values()) {
if (dfsSearch(word, pos+1, child)) {
return true;
}
}
return false;
}
//if character at position 'pos' is neither equal to the node nor '.', return false
if (!node.children.containsKey(word.charAt(pos))) {
return false;
}
return dfsSearch(word, pos + 1, node.children.get(c));
}
Method 2 - Using Array Based Trie Instead of HashMap
We can also use Array-based Trie Implementation.
class TrieNode {
TrieNode[] children;
boolean isLeaf;
public TrieNode() {
children = new TrieNode[26];
}
}
public class WordDictionary {
private final TrieNode root;
public WordDictionary() {
root = new TrieNode();
}
// Adds a word into the data structure.
public void addWord(String word) {
TrieNode p = root;
for (int i = 0; i<word.length(); i++) {
char c = word.charAt(i);
int index = c - 'a';
if (p.children[index] == null) {
TrieNode temp = new TrieNode();
p.children[index] = temp;
}
p = p.children[index];
}
p.isLeaf = true;
}
// Returns if the word is in the data structure. A word could
// contain the dot character '.' to represent any one letter.
public boolean search(String word) {
return dfsSearch(word, 0, root);
}
public boolean dfsSearch(String word, int pos, TrieNode p) {
if (p.isLeaf && pos == word.length())
return true;
if (pos >= word.length())
return false;
char c = word.charAt(pos);
if (c == '.') {
for (TrieNode child: p.children) {
if (child != null) {
if (dfsSearch(word, pos + 1, child)) {
return true;
}
}
}
return false;
}
int index = c - 'a';
if (p.children[index] == null) {
return false;
}
return dfsSearch(word, pos + 1, p.children[index]);
}
}