박종훈 알고리즘 블로그

(Leetcode) 211 - Design Add and Search Words Data Structure (Trie)

기회가 되어 달레님의 스터디에 참여하여 시간이 될 때마다 한문제씩 풀어보고 있다.

https://neetcode.io/practice


https://leetcode.com/problems/design-add-and-search-words-data-structure

점진적으로 개선하여 보았다.

brute force

아슬아슬하게 통과한다.

class WordDictionary {

    Set<String> wordSet;

    public WordDictionary() {
        wordSet = new HashSet<>();
    }

    public void addWord(String word) {
        wordSet.add(word);
    }

    public boolean search(String word) {
        Deque<String> queue = new ArrayDeque<>();
        queue.push(word);

        while (queue.getFirst().contains(".")) {
            String _word = queue.removeFirst();
            String pre = _word.substring(0, _word.indexOf("."));
            String post = _word.substring(_word.indexOf(".") + 1);

            for (char c = 'a'; c <= 'z'; c++) {
                queue.addLast(pre + c + post);
            }
        }

        while (!queue.isEmpty()) {
            String _word = queue.removeFirst();
            if (wordSet.contains(_word)) {
                return true;
            }
        }

        return false;
    }
}

TC, SC

  • . 이 없을 때
    • 시간 복잡도 : O(1)
    • 공간 복잡도 : O(1)
  • . 이 있을 때
    • 시간 복잡도 : O(26^N)
    • 공간 복잡도 : O(26^N)
    • 여기서 N은 . 의 수

trie

208. Implement Trie (Prefix Tree) 문제 에서 사용한 Trie 재사용.

class WordDictionary {

    Trie trie; // Trie 구현은 생략

    public WordDictionary() {
        trie = new Trie();
    }

    public void addWord(String word) {
        trie.insert(word);
    }

    public boolean search(String word) {
        if (word.contains(".")) {
            String pre = word.substring(0, word.indexOf("."));
            String post = word.substring(word.indexOf(".") + 1);

            if (trie.startsWith(pre)) {
                for (char c = 'a'; c <= 'z'; c++) {
                    if (search(pre + c + post)) {
                        return true;
                    }
                }
            }

            return false;
        }

        return trie.search(word);
    }


}

TC, SC

입력된 문자열의 길이를 L, . 의 수를 N 이라고 하였을 때

addWord 메소드의 경우 시간 복잡도는 O(L)이다. search 메소드의 경우 입력된 문자열의 길이를 n 이라 하였을 때 시간 복잡도는 O(L * 26 ^ N)이다.

공간 복잡도는 Trie 구조를 만드는데 사용된 공간이다. insert된 문자열 길이의 평균avg(L)이라고 하였을 때 O(avg(L) * 26)이다. 26은 계수이기 때문에 생략할 수 있다.

trie 개선

이 문제에 적합하도록 search를 수정하였다.

class WordDictionary {

    Trie trie;

    public WordDictionary() {
        trie = new Trie();
    }

    public void addWord(String word) {
        trie.insert(word);
    }

    public boolean search(String word) {
        return trie.search(word);
    }


}

class Trie {

    Node root = new Node();

    public Trie() {

    }

    public void insert(String word) {
        Node currentNode = root;
        for (char c : word.toCharArray()) {
            if (currentNode.nodes[c - 97] == null) {
                currentNode.nodes[c - 97] = new Node();
            }
            currentNode = currentNode.nodes[c - 97];
        }
        currentNode.val = word;
    }

    public boolean search(String word) {
        return search(root, word, 0);
    }

    public boolean search(Node node, String word, int index) {
        if (node == null) {
            return false;
        }

        if (node.val != null && node.val.length() == word.length()) {
            return true;
        }

        if (index >= word.length()) {
            return false;
        }

        char c = word.charAt(index);

        if (c == '.') {
            for (char _c = 'a'; _c <= 'z'; _c++) {
                if (search(node.nodes[_c - 97], word, index + 1)) {
                    return true;
                }
            }
            return false;
        } else if (node.nodes[c - 97] == null) {
            return false;
        }

        return search(node.nodes[c - 97], word, index + 1);
    }
}

class Node {
    String val;
    Node[] nodes = new Node[26];
}

TC, SC

입력된 문자열의 길이를 L, . 의 수를 N 이라고 하였을 때

addWord 메소드의 경우 시간 복잡도는 O(L)이다. search 메소드의 경우 입력된 문자열의 길이를 n 이라 하였을 때 시간 복잡도는 O(L * 26 ^ N)이다. 개선 전과 비교해봤을 때 표기상으로는 차이가 없으나, 불필요한 과정을 제거하게되어서 시간이 매우 단축된다. (trie.startsWith(pre)이 사라졌고, search의 호출 횟수가 줄어듬.)

공간 복잡도는 Trie 구조를 만드는데 사용된 공간이다. insert된 문자열 길이의 평균avg(L)이라고 하였을 때 O(avg(L) * 26)이다. 26은 계수이기 때문에 생략할 수 있다.

submit result

개선된 trie 로직으로 제출결과 최상단에 위치하였다. (약 beats 95%)

submit result