var TrieNode = function() {
this.children = {};
this.isEndOfWord = false;
}
var WordDictionary = function() {
this.root = new TrieNode();
};
/**
* @param {string} word
* @return {void}
*/
WordDictionary.prototype.addWord = function(word) {
let current = this.root;
for(let char of word){
if(!current.children[char]){
current.children[char] = new TrieNode();
}
current = current.children[char];
}
current.isEndOfWord = true;
};
/**
* @param {string} word
* @return {boolean}
*/
WordDictionary.prototype.search = function(word) {
function dfs(node, level){
if(level === word.length){
return node.isEndOfWord;
}
const char = word[level];
if(char === "."){
for(let wordAfterDot of Object.keys(node.children)){
const current = node.children[wordAfterDot];
if(dfs(current, level + 1)){
return true;
}
}
return false;
} else {
if(!node.children[char]){
return false;
}
return dfs(node.children[char], level + 1);
}
}
return dfs(this.root, 0);
};
/**
* Your WordDictionary object will be instantiated and called as such:
* var obj = new WordDictionary()
* obj.addWord(word)
* var param_2 = obj.search(word)
*/
search를 제외하고는 일반적인 Trie의 메서드를 구현하는 것이다.
search는 일반적인 경우라면 Trie의 최상단부터 children을 확인하면서 내려오면 되는데,
이번에는 .이 있는 경우, 어떤 문자열도 될 수 있다는 조건이 붙어있어서 좀 더 깊이 고민해야했다.
.이 나오는 경우에 대해서 살펴보자.
.은 어떤 문자열도 될 수 있기 때문에 모든 경우의 수에 대해서 판단해줘야한다.
우선, 여기서 DFS를 사용하면 될 것 같다는 생각이 들었다.
좀 더 생각해보니, 일반적인 문자열인 경우에도 DFS를 통해 해결하고 있었다는 것을 알 수 있었다.
따라서, 일반적인 경우까지 포함하여 DFS를 구현하는 것이 문제 해결의 핵심이다.
Trie에서 DFS의 base case는 "입력된 문자열의 최대 길이까지 순회를 마쳤는가"이다.
만약 입력된 문자열의 순회를 전부 마쳤음에도 isEndOfWord가 true가 아니라면
그 문자열은 Trie에서 구성할 수 없는 문자열이므로 false, 맞다면 구성할 수 있는 문자열이므로 true를 반환한다.
조건으로 사용되는 isEndOfWord에 따라 boolean 반환값이 변하는데,
반환값이 곧 isEndOfWord와 동일하기 때문에,
굳이 isEndOfWord로 분기를 나눌 필요없이 isEndOfWord를 그대로 반환하면 된다.
.이 아닌 일반적인 문자열이 경우부터 살펴보자.
이 부분은 평범한 Trie의 search 메서드와 동일하다.
입력받은 문자열이 현재 node의 children에 없다면 더 이상 구성할 수 없는 문자열이므로 false,
있다면 그 node로 DFS를 재귀한다. 더 깊게 내려가는 것이다.
앞서, .인 경우는 갈 수 있는 모든 문자열에 대해서 생각해줘야한다고 언급했다.
따라서 현재 node의 children에 들어있는 모든 key를 순회해야한다.
순회하는 노드를 current로 설정하며, current의 다음 children들이 존재하지 않는 값에 대해 연산을 진행한다면 DFS는 false를 반환하므로, 다음으로 가능한 node를 current에 넣어 또 다시 DFS를 진행한다.
만약, 모든 경우의 수를 전부 순회해도 DFS로 문자열을 구성할 수 없다면 최종적으로 false를 반환한다.
많은 분들이 필자와 같이 Object.keys()와 DFS를 사용하여 문제를 해결하셨다.
.이 어디에서든, 몇 번이든 나올 수 있기 때문에 Recursion을 사용하여 문제를 해결하는 것이 굉장히 중요한 포인트였다.