ํŠธ๋ผ์ด (Trie)

ํŠธ๋ผ์ด (Trie)

๊ฐœ์š”

ํŠธ๋ผ์ด(Trie)๋Š” ๋ฌธ์ž์—ด์„ ํšจ์œจ์ ์œผ๋กœ ์ €์žฅํ•˜๊ณ  ๊ฒ€์ƒ‰ํ•˜๋Š” ํŠธ๋ฆฌ ์ž๋ฃŒ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค. ์ ‘๋‘์‚ฌ ํŠธ๋ฆฌ(Prefix Tree)๋ผ๊ณ ๋„ ๋ถˆ๋ฆฌ๋ฉฐ, ์ž๋™์™„์„ฑ, ์‚ฌ์ „ ๊ฒ€์ƒ‰ ๋“ฑ์— ํ™œ์šฉ๋ฉ๋‹ˆ๋‹ค.


๋ชฉ์ฐจ

  1. ํŠธ๋ผ์ด ๊ฐœ๋…
  2. ๊ธฐ๋ณธ ๊ตฌํ˜„
  3. ํŠธ๋ผ์ด ์—ฐ์‚ฐ
  4. XOR ํŠธ๋ผ์ด
  5. ํ™œ์šฉ ๋ฌธ์ œ
  6. ์—ฐ์Šต ๋ฌธ์ œ

1. ํŠธ๋ผ์ด ๊ฐœ๋…

1.1 ๊ตฌ์กฐ

๋‹จ์–ด: "apple", "app", "application", "bat", "ball"

           (root)
          /      \
        a          b
        |          |
        p          a
        |         / \
        p        t   l
       / \       |   |
      l   l      $   l
      |   |          |
      e   i          $
      |   |
      $   c
          |
          a
          |
          t
          |
          i
          |
          o
          |
          n
          |
          $

$ = ๋‹จ์–ด ๋ ํ‘œ์‹œ (isEnd)

ํŠน์ง•:
- ๋ฃจํŠธ๋Š” ๋นˆ ๋…ธ๋“œ
- ๊ฐ ๊ฐ„์„ ์€ ๋ฌธ์ž ํ•˜๋‚˜๋ฅผ ๋‚˜ํƒ€๋ƒ„
- ๊ณตํ†ต ์ ‘๋‘์‚ฌ๋ฅผ ๊ณต์œ 

1.2 ์‹œ๊ฐ„ ๋ณต์žก๋„

m = ๋ฌธ์ž์—ด ๊ธธ์ด

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ ์—ฐ์‚ฐ         โ”‚ ์‹œ๊ฐ„        โ”‚ ์„ค๋ช…            โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ ์‚ฝ์ž…         โ”‚ O(m)        โ”‚ ๋ฌธ์ž์—ด ๊ธธ์ด๋งŒํผ โ”‚
โ”‚ ๊ฒ€์ƒ‰         โ”‚ O(m)        โ”‚ ๋ฌธ์ž์—ด ๊ธธ์ด๋งŒํผ โ”‚
โ”‚ ์ ‘๋‘์‚ฌ ๊ฒ€์ƒ‰  โ”‚ O(m)        โ”‚ ์ ‘๋‘์‚ฌ ๊ธธ์ด๋งŒํผ โ”‚
โ”‚ ์‚ญ์ œ         โ”‚ O(m)        โ”‚ ๋ฌธ์ž์—ด ๊ธธ์ด๋งŒํผ โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๊ณต๊ฐ„: O(์ด ๋ฌธ์ž ์ˆ˜) ๋˜๋Š” O(n ร— m ร— ์•ŒํŒŒ๋ฒณ ํฌ๊ธฐ)

1.3 ํŠธ๋ผ์ด vs ํ•ด์‹œ์…‹

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ ๊ธฐ์ค€           โ”‚ ํŠธ๋ผ์ด       โ”‚ ํ•ด์‹œ์…‹       โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ ๊ฒ€์ƒ‰           โ”‚ O(m)        โ”‚ O(m) ํ‰๊ท     โ”‚
โ”‚ ์ ‘๋‘์‚ฌ ๊ฒ€์ƒ‰    โ”‚ O(p) โœ“      โ”‚ O(n ร— m) โœ—  โ”‚
โ”‚ ์ •๋ ฌ๋œ ์ˆœํšŒ    โ”‚ ๊ฐ€๋Šฅ โœ“      โ”‚ ๋ถˆ๊ฐ€๋Šฅ โœ—    โ”‚
โ”‚ ๊ณต๊ฐ„ ํšจ์œจ      โ”‚ ๋‚ฎ์Œ        โ”‚ ๋†’์Œ         โ”‚
โ”‚ ์ž๋™์™„์„ฑ       โ”‚ ์ตœ์  โœ“      โ”‚ ๋น„ํšจ์œจ โœ—    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

p = ์ ‘๋‘์‚ฌ ๊ธธ์ด, n = ๋‹จ์–ด ์ˆ˜

2. ๊ธฐ๋ณธ ๊ตฌํ˜„

2.1 ๋ฐฐ์—ด ๊ธฐ๋ฐ˜ (๊ณ ์ • ์•ŒํŒŒ๋ฒณ)

class TrieNode:
    def __init__(self):
        self.children = [None] * 26  # a-z
        self.is_end = False

class Trie:
    def __init__(self):
        self.root = TrieNode()

    def _char_to_index(self, c):
        return ord(c) - ord('a')

    def insert(self, word):
        """๋‹จ์–ด ์‚ฝ์ž… - O(m)"""
        node = self.root
        for c in word:
            idx = self._char_to_index(c)
            if node.children[idx] is None:
                node.children[idx] = TrieNode()
            node = node.children[idx]
        node.is_end = True

    def search(self, word):
        """๋‹จ์–ด ๊ฒ€์ƒ‰ - O(m)"""
        node = self.root
        for c in word:
            idx = self._char_to_index(c)
            if node.children[idx] is None:
                return False
            node = node.children[idx]
        return node.is_end

    def starts_with(self, prefix):
        """์ ‘๋‘์‚ฌ ์กด์žฌ ์—ฌ๋ถ€ - O(p)"""
        node = self.root
        for c in prefix:
            idx = self._char_to_index(c)
            if node.children[idx] is None:
                return False
            node = node.children[idx]
        return True


# ์‚ฌ์šฉ ์˜ˆ์‹œ
trie = Trie()
trie.insert("apple")
trie.insert("app")
trie.insert("application")

print(trie.search("app"))       # True
print(trie.search("appl"))      # False
print(trie.starts_with("appl")) # True

2.2 ๋”•์…”๋„ˆ๋ฆฌ ๊ธฐ๋ฐ˜ (์œ ์—ฐํ•œ ์•ŒํŒŒ๋ฒณ)

class TrieNodeDict:
    def __init__(self):
        self.children = {}  # char โ†’ TrieNode
        self.is_end = False

class TrieDict:
    def __init__(self):
        self.root = TrieNodeDict()

    def insert(self, word):
        node = self.root
        for c in word:
            if c not in node.children:
                node.children[c] = TrieNodeDict()
            node = node.children[c]
        node.is_end = True

    def search(self, word):
        node = self.root
        for c in word:
            if c not in node.children:
                return False
            node = node.children[c]
        return node.is_end

    def starts_with(self, prefix):
        node = self.root
        for c in prefix:
            if c not in node.children:
                return False
            node = node.children[c]
        return True

    def delete(self, word):
        """๋‹จ์–ด ์‚ญ์ œ"""
        def _delete(node, word, depth):
            if depth == len(word):
                if not node.is_end:
                    return False  # ๋‹จ์–ด ์—†์Œ
                node.is_end = False
                return len(node.children) == 0

            c = word[depth]
            if c not in node.children:
                return False

            should_delete = _delete(node.children[c], word, depth + 1)

            if should_delete:
                del node.children[c]
                return len(node.children) == 0 and not node.is_end

            return False

        _delete(self.root, word, 0)

2.3 C++ ๊ตฌํ˜„

#include <string>
#include <unordered_map>
using namespace std;

class TrieNode {
public:
    unordered_map<char, TrieNode*> children;
    bool isEnd = false;
};

class Trie {
private:
    TrieNode* root;

public:
    Trie() {
        root = new TrieNode();
    }

    void insert(const string& word) {
        TrieNode* node = root;
        for (char c : word) {
            if (node->children.find(c) == node->children.end()) {
                node->children[c] = new TrieNode();
            }
            node = node->children[c];
        }
        node->isEnd = true;
    }

    bool search(const string& word) {
        TrieNode* node = root;
        for (char c : word) {
            if (node->children.find(c) == node->children.end()) {
                return false;
            }
            node = node->children[c];
        }
        return node->isEnd;
    }

    bool startsWith(const string& prefix) {
        TrieNode* node = root;
        for (char c : prefix) {
            if (node->children.find(c) == node->children.end()) {
                return false;
            }
            node = node->children[c];
        }
        return true;
    }
};

3. ํŠธ๋ผ์ด ์—ฐ์‚ฐ

3.1 ์ž๋™์™„์„ฑ (๋ชจ๋“  ๋‹จ์–ด ์ฐพ๊ธฐ)

class AutocompleteTrie(TrieDict):
    def autocomplete(self, prefix):
        """์ ‘๋‘์‚ฌ๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ชจ๋“  ๋‹จ์–ด ๋ฐ˜ํ™˜"""
        node = self.root
        for c in prefix:
            if c not in node.children:
                return []
            node = node.children[c]

        result = []
        self._collect_words(node, prefix, result)
        return result

    def _collect_words(self, node, current, result):
        if node.is_end:
            result.append(current)

        for c, child in node.children.items():
            self._collect_words(child, current + c, result)


# ์‚ฌ์šฉ ์˜ˆ์‹œ
trie = AutocompleteTrie()
for word in ["apple", "app", "application", "apply", "banana"]:
    trie.insert(word)

print(trie.autocomplete("app"))
# ['app', 'apple', 'application', 'apply']

3.2 ๋‹จ์–ด ๊ฐœ์ˆ˜ ์„ธ๊ธฐ

class CountingTrie:
    def __init__(self):
        self.root = {}

    def insert(self, word):
        node = self.root
        for c in word:
            if c not in node:
                node[c] = {'count': 0}
            node = node[c]
            node['count'] += 1  # ์ด ์ ‘๋‘์‚ฌ๋ฅผ ๊ฐ€์ง„ ๋‹จ์–ด ์ˆ˜
        node['$'] = True  # ๋‹จ์–ด ๋

    def count_prefix(self, prefix):
        """์ ‘๋‘์‚ฌ๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋‹จ์–ด ์ˆ˜"""
        node = self.root
        for c in prefix:
            if c not in node:
                return 0
            node = node[c]
        return node.get('count', 0)

    def count_words(self):
        """์ „์ฒด ๋‹จ์–ด ์ˆ˜"""
        def dfs(node):
            count = 1 if '$' in node else 0
            for c, child in node.items():
                if c not in ['count', '$']:
                    count += dfs(child)
            return count
        return dfs(self.root)


# ์‚ฌ์šฉ ์˜ˆ์‹œ
trie = CountingTrie()
for word in ["apple", "app", "application"]:
    trie.insert(word)

print(trie.count_prefix("app"))  # 3
print(trie.count_prefix("appl"))  # 2
print(trie.count_words())  # 3

3.3 ๊ฐ€์žฅ ๊ธด ๊ณตํ†ต ์ ‘๋‘์‚ฌ

def longest_common_prefix(words):
    """๋ชจ๋“  ๋‹จ์–ด์˜ ๊ฐ€์žฅ ๊ธด ๊ณตํ†ต ์ ‘๋‘์‚ฌ"""
    if not words:
        return ""

    trie = TrieDict()
    for word in words:
        trie.insert(word)

    prefix = []
    node = trie.root

    while True:
        # ์ž์‹์ด ํ•˜๋‚˜์ด๊ณ , ๋‹จ์–ด ๋์ด ์•„๋‹ˆ๋ฉด ๊ณ„์†
        if len(node.children) != 1 or node.is_end:
            break

        c, child = next(iter(node.children.items()))
        prefix.append(c)
        node = child

    return ''.join(prefix)


# ์˜ˆ์‹œ
words = ["flower", "flow", "flight"]
print(longest_common_prefix(words))  # "fl"

3.4 ์™€์ผ๋“œ์นด๋“œ ๊ฒ€์ƒ‰

class WildcardTrie(TrieDict):
    def search_with_wildcard(self, word):
        """'.'์€ ๋ชจ๋“  ๋ฌธ์ž์™€ ๋งค์นญ"""
        return self._search(self.root, word, 0)

    def _search(self, node, word, idx):
        if idx == len(word):
            return node.is_end

        c = word[idx]
        if c == '.':
            # ๋ชจ๋“  ์ž์‹ ํƒ์ƒ‰
            for child in node.children.values():
                if self._search(child, word, idx + 1):
                    return True
            return False
        else:
            if c not in node.children:
                return False
            return self._search(node.children[c], word, idx + 1)


# ์˜ˆ์‹œ
trie = WildcardTrie()
trie.insert("bad")
trie.insert("dad")
trie.insert("mad")

print(trie.search_with_wildcard("pad"))  # False
print(trie.search_with_wildcard("bad"))  # True
print(trie.search_with_wildcard(".ad"))  # True
print(trie.search_with_wildcard("b.."))  # True

4. XOR ํŠธ๋ผ์ด

4.1 ๊ฐœ๋…

XOR ํŠธ๋ผ์ด: ์ •์ˆ˜๋ฅผ ์ด์ง„์ˆ˜๋กœ ์ €์žฅํ•˜๋Š” ํŠธ๋ผ์ด
- ์ตœ๋Œ€ XOR ์Œ ์ฐพ๊ธฐ์— ํ™œ์šฉ
- ๊ฐ ๋น„ํŠธ๋ฅผ ๋†’์€ ์ž๋ฆฌ๋ถ€ํ„ฐ ์ €์žฅ

์˜ˆ์‹œ: 3, 10, 5๋ฅผ ์ €์žฅ (4๋น„ํŠธ)
3  = 0011
10 = 1010
5  = 0101

        root
       /    \
      0      1
     / \      \
    0   1      0
    |   |      |
    1   0      1
    |   |      |
    1   1      0
    โ†“   โ†“      โ†“
    3   5      10

4.2 ์ตœ๋Œ€ XOR ์Œ

class XORTrie:
    def __init__(self, max_bits=30):
        self.root = {}
        self.max_bits = max_bits

    def insert(self, num):
        """์ˆซ์ž ์‚ฝ์ž…"""
        node = self.root
        for i in range(self.max_bits - 1, -1, -1):
            bit = (num >> i) & 1
            if bit not in node:
                node[bit] = {}
            node = node[bit]

    def find_max_xor(self, num):
        """num๊ณผ XORํ–ˆ์„ ๋•Œ ์ตœ๋Œ€๊ฐ’์„ ๋งŒ๋“œ๋Š” ์ˆ˜์˜ XOR ๊ฒฐ๊ณผ"""
        node = self.root
        result = 0

        for i in range(self.max_bits - 1, -1, -1):
            bit = (num >> i) & 1
            # ๋ฐ˜๋Œ€ ๋น„ํŠธ๋ฅผ ์„ ํƒํ•˜๋ฉด XOR์ด ์ตœ๋Œ€
            opposite = 1 - bit

            if opposite in node:
                result |= (1 << i)
                node = node[opposite]
            elif bit in node:
                node = node[bit]
            else:
                break

        return result


def find_maximum_xor(nums):
    """๋ฐฐ์—ด์—์„œ ์ตœ๋Œ€ XOR ์Œ ์ฐพ๊ธฐ"""
    if len(nums) < 2:
        return 0

    trie = XORTrie()
    max_xor = 0

    for num in nums:
        trie.insert(num)
        max_xor = max(max_xor, trie.find_max_xor(num))

    return max_xor


# ์˜ˆ์‹œ
nums = [3, 10, 5, 25, 2, 8]
print(find_maximum_xor(nums))  # 28 (5 XOR 25 = 28)

4.3 ๊ตฌ๊ฐ„ XOR ์ตœ๋Œ€

class PersistentXORTrie:
    """
    ๊ตฌ๊ฐ„ [l, r]์—์„œ k์™€ XOR ์ตœ๋Œ€๊ฐ’
    ์˜คํ”„๋ผ์ธ ์ฟผ๋ฆฌ + Persistent Trie
    """
    def __init__(self, max_bits=30):
        self.max_bits = max_bits
        self.nodes = [[0, 0]]  # [left_child, right_child]
        self.count = [0]  # ๊ฐ ๋…ธ๋“œ๋ฅผ ์ง€๋‚˜๋Š” ์ˆซ์ž ๊ฐœ์ˆ˜
        self.roots = [0]  # ๋ฒ„์ „๋ณ„ ๋ฃจํŠธ

    def insert(self, prev_root, num):
        """์ด์ „ ๋ฒ„์ „์—์„œ num ์ถ”๊ฐ€"""
        new_root = len(self.nodes)
        self.nodes.append([0, 0])
        self.count.append(0)

        curr = new_root
        prev = prev_root

        for i in range(self.max_bits - 1, -1, -1):
            bit = (num >> i) & 1

            # ์ƒˆ ๋…ธ๋“œ ์ƒ์„ฑ
            child = len(self.nodes)
            self.nodes.append([0, 0])
            self.count.append(0)

            # ๋ฐ˜๋Œ€์ชฝ ์ž์‹์€ ์ด์ „ ๋ฒ„์ „์—์„œ ๋ณต์‚ฌ
            self.nodes[curr][1 - bit] = self.nodes[prev][1 - bit] if prev else 0
            self.nodes[curr][bit] = child

            # ์นด์šดํŠธ ์—…๋ฐ์ดํŠธ
            self.count[child] = (self.count[self.nodes[prev][bit]] if prev else 0) + 1

            curr = child
            prev = self.nodes[prev][bit] if prev else 0

        self.roots.append(new_root)
        return new_root

    def query(self, l_root, r_root, num):
        """๋ฒ„์ „ (l, r] ์‚ฌ์ด์—์„œ num๊ณผ ์ตœ๋Œ€ XOR"""
        result = 0
        l_node = l_root
        r_node = r_root

        for i in range(self.max_bits - 1, -1, -1):
            bit = (num >> i) & 1
            opposite = 1 - bit

            l_opp_count = self.count[self.nodes[l_node][opposite]] if l_node else 0
            r_opp_count = self.count[self.nodes[r_node][opposite]] if r_node else 0

            if r_opp_count - l_opp_count > 0:
                result |= (1 << i)
                l_node = self.nodes[l_node][opposite] if l_node else 0
                r_node = self.nodes[r_node][opposite]
            else:
                l_node = self.nodes[l_node][bit] if l_node else 0
                r_node = self.nodes[r_node][bit]

        return result

5. ํ™œ์šฉ ๋ฌธ์ œ

5.1 ๋‹จ์–ด ์ถ”๊ฐ€ ๋ฐ ๊ฒ€์ƒ‰

# LeetCode 211. Design Add and Search Words Data Structure
class WordDictionary:
    def __init__(self):
        self.trie = WildcardTrie()

    def addWord(self, word):
        self.trie.insert(word)

    def search(self, word):
        return self.trie.search_with_wildcard(word)

5.2 ๋‹จ์–ด ๋Œ€์ฒดํ•˜๊ธฐ

def replace_words(dictionary, sentence):
    """
    ๋ฌธ์žฅ์˜ ๊ฐ ๋‹จ์–ด๋ฅผ ์‚ฌ์ „์˜ ์ ‘๋‘์‚ฌ๋กœ ๋Œ€์ฒด
    dictionary = ["cat", "bat", "rat"]
    sentence = "the cattle was rattled by the battery"
    โ†’ "the cat was rat by the bat"
    """
    trie = TrieDict()
    for word in dictionary:
        trie.insert(word)

    def find_root(word):
        node = trie.root
        for i, c in enumerate(word):
            if c not in node.children:
                return word
            node = node.children[c]
            if node.is_end:
                return word[:i + 1]
        return word

    words = sentence.split()
    return ' '.join(find_root(word) for word in words)


# ์˜ˆ์‹œ
dictionary = ["cat", "bat", "rat"]
sentence = "the cattle was rattled by the battery"
print(replace_words(dictionary, sentence))
# "the cat was rat by the bat"

5.3 ์—ฐ๊ฒฐ ๋‹จ์–ด

def find_all_concatenated_words(words):
    """
    ๋‹ค๋ฅธ ๋‹จ์–ด๋“ค์„ ์ด์–ด๋ถ™์—ฌ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” ๋‹จ์–ด ์ฐพ๊ธฐ
    """
    trie = TrieDict()
    for word in words:
        if word:
            trie.insert(word)

    def can_form(word, start, count):
        if start == len(word):
            return count >= 2

        node = trie.root
        for i in range(start, len(word)):
            c = word[i]
            if c not in node.children:
                return False
            node = node.children[c]
            if node.is_end:
                if can_form(word, i + 1, count + 1):
                    return True

        return False

    result = []
    for word in words:
        if word and can_form(word, 0, 0):
            result.append(word)

    return result


# ์˜ˆ์‹œ
words = ["cat", "cats", "catsdogcats", "dog", "dogcatsdog",
         "hippopotamuses", "rat", "ratcatdogcat"]
print(find_all_concatenated_words(words))
# ["catsdogcats", "dogcatsdog", "ratcatdogcat"]

5.4 Suffix Trie (์ ‘๋ฏธ์‚ฌ ํŠธ๋ผ์ด)

class SuffixTrie:
    """๋ฌธ์ž์—ด์˜ ๋ชจ๋“  ์ ‘๋ฏธ์‚ฌ๋ฅผ ์ €์žฅํ•˜๋Š” ํŠธ๋ผ์ด"""

    def __init__(self, text):
        self.root = {}
        self._build(text)

    def _build(self, text):
        for i in range(len(text)):
            node = self.root
            for c in text[i:]:
                if c not in node:
                    node[c] = {}
                node = node[c]
            node['$'] = i  # ์‹œ์ž‘ ์ธ๋ฑ์Šค ์ €์žฅ

    def search(self, pattern):
        """ํŒจํ„ด์ด ์กด์žฌํ•˜๋Š” ๋ชจ๋“  ์‹œ์ž‘ ์œ„์น˜"""
        node = self.root
        for c in pattern:
            if c not in node:
                return []
            node = node[c]

        # ์ด ๋…ธ๋“œ ์•„๋ž˜์˜ ๋ชจ๋“  $ ์ˆ˜์ง‘
        result = []
        self._collect(node, result)
        return result

    def _collect(self, node, result):
        if '$' in node:
            result.append(node['$'])
        for c, child in node.items():
            if c != '$':
                self._collect(child, result)


# ์˜ˆ์‹œ
st = SuffixTrie("banana")
print(st.search("ana"))  # [1, 3]
print(st.search("nan"))  # [2]

6. ์—ฐ์Šต ๋ฌธ์ œ

์ถ”์ฒœ ๋ฌธ์ œ

๋‚œ์ด๋„ ๋ฌธ์ œ ํ”Œ๋žซํผ ์œ ํ˜•
โญโญ Implement Trie LeetCode ๊ธฐ๋ณธ ๊ตฌํ˜„
โญโญโญ ์ „ํ™”๋ฒˆํ˜ธ ๋ชฉ๋ก ๋ฐฑ์ค€ ์ ‘๋‘์‚ฌ
โญโญโญ Design Search Autocomplete LeetCode ์ž๋™์™„์„ฑ
โญโญโญ Add and Search Word LeetCode ์™€์ผ๋“œ์นด๋“œ
โญโญโญโญ Maximum XOR of Two Numbers LeetCode XOR ํŠธ๋ผ์ด
โญโญโญโญ ๋ฌธ์ž์—ด ์ง‘ํ•ฉ ๋ฐฑ์ค€ ๊ฒ€์ƒ‰

์‹œ๊ฐ„/๊ณต๊ฐ„ ๋ณต์žก๋„

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ ์—ฐ์‚ฐ             โ”‚ ์‹œ๊ฐ„        โ”‚ ๊ณต๊ฐ„                 โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ ์‚ฝ์ž…             โ”‚ O(m)        โ”‚ O(m ร— ์•ŒํŒŒ๋ฒณ)        โ”‚
โ”‚ ๊ฒ€์ƒ‰             โ”‚ O(m)        โ”‚ -                   โ”‚
โ”‚ ์ ‘๋‘์‚ฌ ๊ฒ€์ƒ‰      โ”‚ O(p)        โ”‚ -                   โ”‚
โ”‚ ์ž๋™์™„์„ฑ         โ”‚ O(p + k)    โ”‚ O(๊ฒฐ๊ณผ ๊ธธ์ด)         โ”‚
โ”‚ XOR ์ตœ๋Œ€         โ”‚ O(log MAX)  โ”‚ O(n ร— log MAX)      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

m = ๋‹จ์–ด ๊ธธ์ด, p = ์ ‘๋‘์‚ฌ ๊ธธ์ด, k = ๊ฒฐ๊ณผ ๊ฐœ์ˆ˜

๋‹ค์Œ ๋‹จ๊ณ„


์ฐธ๊ณ  ์ž๋ฃŒ

to navigate between lessons