Tries & Suffix Trees
문자열의 접두사와 접미사를 트리 형태로 구조화하여 검색과 패턴 매칭의 물리적 효율을 극대화하는 텍스트 인덱싱 기술을 다루는 학습 노드입니다.
Article
M
Me
hyunyoun's Blog
data-structures-algorithmsdata-structuresalgorithmscore-data-structurestriessuffix-treesadvanced-data-structureslearning9 min read
1. Overview
트라이와 접미사 트리(Tries & Suffix Trees)는 문자열(String) 집합에 대한 탐색, 접두사(Prefix) 자동완성, 패턴 매칭을 O(m) 시간(m=검색 문자열 길이)으로 수행하는, 문자를 트리 엣지(Edge)로 인코딩한 극한의 문자열 인덱스 공학입니다.
학습자는 사전(Dictionary)에 있는 10만 개의 단어 중 "app"으로 시작하는 모든 단어를 찾을 때, 해시 테이블이 O(n)의 전수 탐색을 요구하는 반면 트라이(Trie)가 O(m)(m="app"의 길이=3)에 찾아내는 접두사 압축 역학을 뜯어봅니다. 나아가 텍스트 전체를 전처리하여 임의의 부분 문자열을 O(m)에 찾는 **접미사 트리(Suffix Tree)와 접미사 배열(Suffix Array)**의 정보 압축 기하학을 해부하여, 검색 엔진 자동완성, DNA 시퀀스 분석, 안티바이러스 패턴 매칭 엔진의 내부를 통달하는 역량을 확보합니다.
2. Scope & Boundaries
In-Scope
- 트라이 (Trie / Prefix Tree): 노드 구조(
children[26],is_end), 삽입/탐색/삭제 O(m), 접두사 검색, 공통 접두사 길이(LCP). - 압축 트라이 (Compressed Trie / Radix Tree / Patricia Tree): 단일 자식 노드 체인 압축, 라우팅 테이블(IP Routing Table) 최장 접두사 매칭(LPM).
- 접미사 자료구조 (Suffix Structures): 접미사 배열(Suffix Array, SA), LCP 배열(Longest Common Prefix), Ukkonen's O(n) 접미사 트리 구축.
- 실무 응용 (Real-world Applications): 자동완성(Autocomplete), 철자 교정(Spell Checker), 침입 탐지 시스템(IDS) 다중 패턴 매칭(Aho-Corasick).
Out-of-Scope
- KMP, Rabin-Karp 문자열 매칭: 단일 패턴 매칭 알고리즘 04-04-02. String Matching & Automata 영역.
- B-트리 기반 문자열 인덱스: 데이터베이스의 Like 쿼리 최적화 06-01. Database Index Internals 영역.
Boundaries
- Trie vs. Hash Map for String Search: 해시맵은 단어 전체가 정확히 일치하는 경우(Exact Match)에 O(1)이지만 접두사 검색은 불가능합니다. 트라이는 접두사 검색 O(m)이 완벽하지만, 노드마다 자식 배열(
children[26])을 유지해야 하므로 알파벳이 큰 경우(예: 한글, 유니코드) 메모리 사용량이 폭발하는 대가를 치릅니다.
3. Counterexample
- 일반 Trie의 메모리 폭발 (Trie Memory Explosion): 알파벳이 256가지(ASCII 전체)인 경우, 각 노드가
children[256]배열을 가지므로 빈 자식 포인터만으로 노드 1개당 256 * 8Byte = 2KB를 차지합니다. 단어 10만 개를 삽입하면 노드가 수백만 개 생기며 수 기가바이트 메모리를 소비할 수 있습니다. 영어 소문자(26가지)에 최적화된 트라이를 유니코드 범용 문자열에 무비판적으로 적용하는 실수입니다. 해시맵(children = {})을 자식 저장소로 쓰거나 압축 트라이를 써서 해결해야 합니다. - 접미사 트리 Naive O(n²) 구축의 함정 (Naive Suffix Tree Construction): 길이 n=100만인 DNA 서열에 대해, 모든 n개의 접미사를 하나씩 트라이에 삽입하는 Naive 방법으로 접미사 트리를 만들려는 시도. 삽입 1회가 O(n)이므로 전체 O(n²) = 10^12 연산으로 수천 초가 걸립니다. Ukkonen's 알고리즘(O(n) 선형 시간 접미사 트리 구축)을 사용해야 하지만 구현 난이도가 극도로 높습니다.
4. Prerequisites
- 재귀와 트리 (Basic): 트라이는 트리 자료구조이므로 재귀 탐색의 기초가 필요합니다. (04-02-01 Binary Trees, 04-03-01 Recursion)
- 해시맵 (Recommended): 압축 트라이에서
children을 해시맵으로 구현하는 패턴을 이해하기 위해 필요합니다. (04-02-03 Hash Tables)
5. Learning Map
6. Learning Topics
Basic
Core Topic 01: 문자 경로와 단어의 지도, 트라이 기초 (Trie Fundamentals)
- Why to Learn: 검색창에 "app"을 치는 순간 "apple", "application", "appreciate" 등이 즉각 드롭다운되는 자동완성 기능이, 10만 개의 단어를 O(3) 시간에 탐색하는 트라이의 접두사 압축 마법임을 꿰기 위함입니다.
- What to Learn:
- Concepts: TrieNode(
children[26],is_end), 삽입(Insert), 탐색(Search), 접두사 탐색(StartsWith). - Skills: DFS로 모든 단어 열거, 삭제(Delete, 역방향 DFS로 후손 없는 노드 제거).
- Tools: Python
defaultdict(dict)기반 트라이 구현.
- Concepts: TrieNode(
- How to Learn:
- 1단계: "apple"과 "app" 삽입. 노드 경로: root → a → p → p →
is_end=True(app) → l → e →is_end=True(apple). "app" 탐색 시 root→a→p→p까지 이동,is_end=True확인 → 존재. "application" 탐색 시 root→a→p→p→l →(없음) → 탐색 실패 역학을 해부합니다. - 2단계: 접두사 탐색
startsWith("ap"): root→a→p까지 이동 성공하면 "ap"로 시작하는 단어가 있음을 반환. 이후 DFS로 하위 모든 단어(is_end=True경로 목록)를 수집하는 자동완성 구현을 뜯어봅니다.
- 1단계: "apple"과 "app" 삽입. 노드 경로: root → a → p → p →
- Implement: 파이썬
Trie클래스.TrieNode(children=defaultdict(TrieNode), is_end=False).insert(word),search(word),starts_with(prefix),autocomplete(prefix)구현.["apple","app","application","apply"]삽입 후autocomplete("app")→["app","apple","application","apply"]검증.
Recommended
Core Topic 02: 체인 압축과 최장 접두사, 압축 트라이 (Radix Tree & IP Routing)
- Why to Learn: 인터넷 라우터가 수백만 개의 IP 주소 접두사(192.168.1.0/24) 중 패킷의 목적지와 가장 긴 접두사가 일치하는 다음 홉(Next Hop)을 O(m) 시간에 찾는 **최장 접두사 매칭(LPM, Longest Prefix Match)**이 압축 트라이(Radix Tree) 기반임을 장악하기 위해서입니다.
- What to Learn:
- Concepts: 압축 트라이(Radix Tree / Patricia Tree), 엣지에 문자열 저장, 최장 접두사 매칭(LPM).
- Skills: 공통 접두사(LCP) 계산, 노드 분할(Split) 연산.
- Tools: Linux 커널
lib/radix-tree.c.
- How to Learn:
- 1단계: 일반 트라이에서 "application"을 삽입하면 11개의 노드가 생기는 반면, 압축 트라이(Radix)에서는 "applica"가 하나의 엣지 라벨이 되어 단 몇 개의 노드로 표현됩니다. 새 단어 "apple"이 들어오면 공통 접두사 "appl"에서 노드를 분할(Split)하는 역학을 해부합니다.
- 2단계: IP 라우팅에서 목적지
192.168.1.100에 대해 라우팅 테이블[192.168.1.0/24, 192.168.0.0/16, 192.0.0.0/8]중 가장 긴 접두사192.168.1.0/24를 Radix Tree를 통해 O(32)(IP 비트 수)에 찾는 LPM 역학을 뜯어봅니다.
- Implement: 파이썬
RadixTree클래스(단순화).insert(word)시 기존 노드와 공통 접두사 LCP 계산 후 분할.search(word)O(m) 구현.["test","testing","tester","slow","slowly"]삽입 후"testing"탐색,"test"접두사 모든 단어 열거 검증.
Practical
Core Topic 03: 모든 접미사의 거울, 접미사 배열과 LCP (Suffix Array & LCP)
- Why to Learn: 게놈 서열 분석("ATCG" 패턴 반복 탐색), 표절 검사(최장 공통 부분 문자열), 데이터 압축(LZ77, Bzip2)의 핵심 엔진인 접미사 배열을 O(n log n) 이내로 구축하는 역학을 꿰기 위함입니다.
- What to Learn:
- Concepts: 접미사 배열(Suffix Array, SA), 접미사(Suffix), SA 정렬 O(n log²n) Naive / O(n log n) Prefix Doubling.
- Skills: LCP 배열(Kasai's Algorithm, O(n)), SA+LCP로 최장 반복 부분 문자열 탐색.
- Tools: Python
sorted([(s[i:],i) for i in range(n)])Naive SA 구축.
- How to Learn:
- 1단계: 문자열 "banana"의 모든 접미사
["banana","anana","nana","ana","na","a"]를 사전순 정렬 →["a","ana","anana","banana","na","nana"]. SA =[5,3,1,0,4,2](정렬된 접미사의 시작 인덱스). "ana"가 텍스트에 등장하는 위치를 SA 이진 탐색으로 O(m log n)에 찾는 역학을 해부합니다. - 2단계: LCP 배열: 연속된 두 정렬 접미사의 공통 접두사 길이.
LCP[2]=3(anana와 banana의 LCP=0... "a"와 "ana"의 LCP=1). Kasai's Algorithm으로 O(n) LCP 배열 구축 역학을 뜯어봅니다.
- 1단계: 문자열 "banana"의 모든 접미사
- Implement: 파이썬
build_suffix_array(s). Naive:sorted(range(len(s)), key=lambda i: s[i:]). 문자열 "mississippi"의 SA를 구축하고, 이를 이용해 패턴 "issi" 등장 위치를bisect(이진 탐색)로 O(m log n)에 탐색하는 검색 엔진 구현.
Advanced
Core Topic 04: 실패에서 배우는 다중 패턴 탐색, Aho-Corasick 알고리즘 (Aho-Corasick)
- Why to Learn: 안티바이러스 소프트웨어가 10만 개의 악성코드 패턴을 텍스트 1번 스캔으로 동시에 탐색하거나, 네트워크 침입 탐지 시스템(IDS/IPS)이 실시간 패킷 페이로드에서 수천 개의 악성 시그니처를 동시에 찾는 핵심 알고리즘을 장악하기 위해서입니다.
- What to Learn:
- Concepts: Aho-Corasick 오토마톤, 트라이 + 실패 링크(Failure Links) + 출력 링크(Output Links), BFS로 실패 링크 구축, O(n+m+z) 탐색(n=텍스트, m=패턴 합계, z=매칭 수).
- Skills: 실패 링크를 KMP의 실패 함수(Failure Function)와 연결하여 이해.
- Tools: Aho-Corasick 라이브러리(
pyahocorasick).
- How to Learn:
- 1단계: 패턴
["he","she","his","hers"]를 트라이에 삽입. BFS로 실패 링크 구축: 탐색이 실패할 때 "현재 접두사의 가장 긴 적절한 접미사이면서 트라이에 있는 노드"로 점프하는 KMP-like 실패 회복 역학을 해부합니다. - 2단계: 텍스트 "ahishers"를 단 한 번 선형 스캔하며 패턴
["his", "she", "hers"]3개가 각각 텍스트 어디서 등장하는지 동시에 찾아내는 O(n+m+z) 시간 복잡도 증명을 뜯어봅니다.
- 1단계: 패턴
- Implement: 파이썬
AhoCorasick클래스.insert(patterns)후build_failure_links()(BFS).search(text)로"ushers"텍스트에서["he","she","his","hers","ush"]패턴 매칭 결과[(2,"he"),(1,"she"),(3,"hers")]등 위치+패턴 쌍 출력. 패턴 5개를 각각 개별 탐색하는 O(5n) vs Aho-Corasick O(n+m+z) 속도 비교.
7. Terminology
8. References
Primary
- [P2] SWEBOK v4.0 - Software Construction / Runtime Efficiency (Search) — Search optimization.
- [P1] CS2023 - AL/Algorithms and Complexity (String Processing) — Core requirements.
Secondary
- [Algorithms on Strings, Trees and Sequences] Dan Gusfield — The suffix tree bible.
- [Advanced Data Structures] Peter Brass — Advanced trie variants.
Industry
- [Cloudflare: Use of Radix Trees for IP Routing] — Networking case study.
- [Google: Auto-complete Architecture with Tries] — Real-world application.
9. Final Checklist
Primary
- '트라이' 자료구조에서 특정 단어의 존재 여부를 확인할 때의 시간 복잡도가 왜 '데이터 개수'가 아닌 '단어 길이'에 의존하는지 설명 가능한가? (P1)
- '접미사 트리'를 이용해 문서 내에서 특정 패턴을 찾는 무궁무진한 과정의 물리적 복잡도를 입증할 수 있는 가? (P1)
Secondary
- 압축되지 않은 트라이가 왜 실무 대규모 데이터셋에서 '메모리 단편화'와 '공간 낭비'를 유발하는지 물리적 근거로 소통 가능한가?
- 접미사 트리와 접미사 배열 중, 메모리가 극도로 제한된 임베디드 환경에서 어느 것을 선택할지 수리적 트레이드-오프를 도출할 수 있는 가?
Industry
- 검색 엔진의 '검색어 추천' 기능을 설계할 때, 사용자 로그를 트라이에 동적으로 반영하는 물리적 갱신 시나리오를 제안할 수 있는 가? (SFIA)
- 유전체 서열 분석(Alignment) 시, 수조 개의 염기서열 속에서 특정 유전자를 빛의 속도로 찾는 BWT 인덱스의 물리적 기여도를 기술할 수 있는 가?