package com.freetymekiyan.algorithms.level.medium; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; /** * Design a data structure that supports the following two operations: * <p> * 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. * <p> * For example: * <p> * addWord("bad") * addWord("dad") * addWord("mad") * search("pad") -> false * search("bad") -> true * search(".ad") -> true * search("b..") -> true * <p> * Note: * You may assume that all words are consist of lowercase letters a-z. * <p> * You should be familiar with how a Trie works. If not, please work on this problem: Implement Trie (Prefix Tree) * first. * <p> * Company Tags: Facebook * Tags: Backtracking, Trie, Design * Similar Problems: (M) Implement Trie (Prefix Tree) */ public class AddAndSearchWord { private WordDictionary d; @Before public void setUp() { d = new WordDictionary(); } @Test public void testEdgeCase() { d.addWord("a"); Assert.assertTrue(d.search(".")); d.addWord("ab"); Assert.assertTrue(d.search("a")); Assert.assertTrue(d.search("a.")); } @After public void tearDown() { d = null; } /** * Trie. * Create a trie in the word dictionary class. */ public class WordDictionary { TrieNode root = new TrieNode(); // Adds a word into the data structure. public void addWord(String word) { if (word == null || word.length() == 0) { return; } TrieNode node = root; for (int i = 0; i < word.length(); i++) { char c = word.charAt(i); if (node.links[c - 'a'] == null) { node.links[c - 'a'] = new TrieNode(); } node = node.links[c - 'a']; } node.isEnd = 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) { if (word == null) { return false; } return searchPrefix(word, 0, root); } /** * Backtracking. * Statement: Given a word, a position, and a trie node, find whether the word is in the trie. * Recurrent Relation: * The word is in the trie if: Current character at pos match + Other characters from pos + 1 are in too. * Base case: * When subset is empty, return whether the node is end. * Current char can be '.' or a letter. * If it's not dot: * | Get the next node. * | Return next is not null && search(word, pos + 1, next). * <p> * If it's dot, how to deal with it? '.' can match any character. * | As long as current node has non-null link, search the rest of the prefix in trie. * | If one of them returns true, return true. * Return false. */ private boolean searchPrefix(String word, int pos, TrieNode node) { if (pos == word.length()) { return node.isEnd; } if (word.charAt(pos) == '.') { for (int i = 0; i < node.links.length; i++) { if (node.links[i] != null && searchPrefix(word, pos + 1, node.links[i])) { return true; } } } else { TrieNode next = node.links[word.charAt(pos) - 'a']; return next != null && searchPrefix(word, pos + 1, next); } return false; } class TrieNode { private final int R = 26; TrieNode[] links; boolean isEnd; public TrieNode() { links = new TrieNode[R]; } } } // Your WordDictionary object will be instantiated and called as such: // WordDictionary wordDictionary = new WordDictionary(); // wordDictionary.addWord("word"); // wordDictionary.search("pattern"); }