[LeetCode | Javascript] Design Add and Search Words Data Structure

박기영·2023년 9월 10일

LeetCode

목록 보기
33/41

solution

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에서 DFSbase case는 "입력된 문자열의 최대 길이까지 순회를 마쳤는가"이다.
만약 입력된 문자열의 순회를 전부 마쳤음에도 isEndOfWordtrue가 아니라면
그 문자열은 Trie에서 구성할 수 없는 문자열이므로 false, 맞다면 구성할 수 있는 문자열이므로 true를 반환한다.
조건으로 사용되는 isEndOfWord에 따라 boolean 반환값이 변하는데,
반환값이 곧 isEndOfWord와 동일하기 때문에,
굳이 isEndOfWord로 분기를 나눌 필요없이 isEndOfWord를 그대로 반환하면 된다.

.이 아닌 일반적인 문자열이 경우부터 살펴보자.
이 부분은 평범한 Triesearch 메서드와 동일하다.
입력받은 문자열이 현재 nodechildren에 없다면 더 이상 구성할 수 없는 문자열이므로 false,
있다면 그 nodeDFS를 재귀한다. 더 깊게 내려가는 것이다.

앞서, .인 경우는 갈 수 있는 모든 문자열에 대해서 생각해줘야한다고 언급했다.
따라서 현재 nodechildren에 들어있는 모든 key를 순회해야한다.
순회하는 노드를 current로 설정하며, current의 다음 children들이 존재하지 않는 값에 대해 연산을 진행한다면 DFSfalse를 반환하므로, 다음으로 가능한 nodecurrent에 넣어 또 다시 DFS를 진행한다.
만약, 모든 경우의 수를 전부 순회해도 DFS로 문자열을 구성할 수 없다면 최종적으로 false를 반환한다.

많은 분들이 필자와 같이 Object.keys()DFS를 사용하여 문제를 해결하셨다.
.이 어디에서든, 몇 번이든 나올 수 있기 때문에 Recursion을 사용하여 문제를 해결하는 것이 굉장히 중요한 포인트였다.

profile
나를 믿는 사람들을, 실망시키지 않도록

0개의 댓글