JavaScript using Trie and DFS, Heavily Commented Clear Solution
\nfunction Node () {\n    // children object to store children nodes\n    this.children = {};\n    \n    // boolean to check if the current node represents a le
```\nfunction Node () {\n    // children object to store children nodes\n    this.children = {};\n    \n    // boolean to check if the current node represents a letter that is the ending of a word\n    this.isWordEnding = false;\n    \n    // note: you might also want to store this.char here for debugging purposes or for different problems.\n}\n\nvar WordDictionary = function() {\n    // the root is a node itself.\n    this.root = new Node();\n};\n\nWordDictionary.prototype.addWord = function(word) {\n    // we will traverse the trie starting from the root node and add nodes for each letter in word.\n    let currentNode = this.root;\n    \n    for (const char of word) {\n        // if a node exists for a given letter then don\'t do anything.\n        // if not create a new node for that letter.\n        currentNode.children[char] = currentNode.children[char] || new Node();\n        \n        // move on to the next node.\n        currentNode = currentNode.children[char];\n    }\n    \n    // after looping, the currentNode variable will point to the node representing the last letter of word.\n    // so we mark that node as a word ending.\n    currentNode.isWordEnding = true;\n};\n\nWordDictionary.prototype.search = function(word) {\n    // helper function to call recursively\n    const searchHelper = (currentNode, i) => {\n        \n        // if we reach the i that\'s the length of word and currentNode is a word ending, word exists.\n        if (i === word.length) return currentNode.isWordEnding\n        \n        const char = word[i]\n        \n        // if char is a dot, that means we can match it with any letter.\n        // to do that programmatically, we go through all of the children of the current node. why?\n        // we don\'t know which, if any, of the children can use the dot to make the given string.\n        // so we go through all of them and check if any of them can return true.\n        if (char === \'.\') {\n            for (const char of Object.keys(currentNode.children)) {\n                const child = currentNode.children[char];\n                if (searchHelper(child, i + 1)) return true\n            }\n            \n            // if no child can make use of the dot to come up with the given word,\n            // then even the alternative version (e.g \'pad\') \n            // of the given string (e.g \'pa.\') doesn\'t exist in our dictionary.\n            return false\n        } \n        \n        // if char isn\'t a dot, it\'s more straightforward...\n        else {\n            // looking for a letter that should come after another and can\'t find it?\n            // that means the word doesn\'t exist in our dictionary so return false.\n            if (!(char in currentNode.children)) return false\n            \n            // go on to the next node in our dictionary and the next letter in the word\n            return searchHelper(currentNode.children[char], i + 1)\n        }\n    }\n    \n    // we call this function by starting at our root node with the index for the first letter in the string\n    return searchHelper(this.root, 0)\n};\n```
['Backtracking', 'Depth-First Search', 'Trie', 'Recursion', 'JavaScript']