Workspace IndexAlgorithms › Day 9

String Indexing TODO

Algorithms · Day 9 / 100 · A. Advanced Algorithms & Data Structures (Day 1-19)

Concept

A suffix array is an index that sorts all suffixes of a string lexicographically and stores just their starting positions in an array. Because it's sorted, an arbitrary pattern search can be done by binary search, and pairing it with an LCP array — the longest common prefix length between adjacent suffixes — lets you answer queries like repeated substrings or the count of distinct substrings in near-linear time. A suffix automaton (DAWG) encodes the same information as a state machine: it's the minimal deterministic automaton that recognizes every substring of the string, and its state count stays linear in the input length. An automaton can be built online, one character at a time, which makes it well suited to streaming, while a suffix array's compact memory footprint favors large static text. What both share at their core is turning "scan for the pattern every time" into "preprocess the text once and answer queries in constant or logarithmic time."

When the text is fixed — logs, traces — but search happens over and over, a grep-style linear scan turns straight into a cost the moment the data grows.

Code & Formula

# Day 9: 문자열 인덱스 — 서픽스 배열로 부분 문자열 검색을 이분 탐색으로 처리
# 모든 접미사를 정렬해 배열로 두면, 패턴 검색이 선형 스캔 대신 O(log n · m)에 끝난다.

def build_suffix_array(text):
    return sorted(range(len(text)), key=lambda i: text[i:])

def search(text, sa, pattern):
    lo, hi = 0, len(sa)
    while lo < hi:
        mid = (lo + hi) // 2
        if text[sa[mid]:sa[mid] + len(pattern)] < pattern:
            lo = mid + 1
        else:
            hi = mid
    if lo == len(sa) or text[sa[lo]:sa[lo] + len(pattern)] != pattern:
        return []
    hits, i = [sa[lo]], lo + 1
    while i < len(sa) and text[sa[i]:sa[i] + len(pattern)] == pattern:
        hits.append(sa[i]); i += 1
    return sorted(hits)

text = "the quick brown fox jumps over the lazy dog the fox runs"
sa = build_suffix_array(text)
print("서픽스 배열(앞 10개 시작 위치) =", sa[:10])
for pattern in ["fox", "the", "cat"]:
    print(f"search('{pattern}') -> 위치 {search(text, sa, pattern)}")

Exercise

Build a suffix array plus LCP array for a 10MB log file, then run the same 1,000 substring queries via plain linear scan versus binary search over the suffix array, and compare the timings.

Practical Connection

When you're repeatedly searching node logs or transaction traces for a specific address, function selector, or event signature, building the index once is operationally far more stable than doing a full scan every time.

Where it lands in Jayverse

Key expressions

Words and phrases from this page worth keeping, with the Korean meaning and the sentence they come from.

Expression뜻 · 쓰이는 자리
lexicographically사전순으로 · 접미사들을 알파벳 순서로 정렬한다는 뜻. "sorts all suffixes of a string lexicographically"
well suited to~에 잘 맞는, 적합한 · 온라인 빌드 방식이 스트리밍에 알맞다는 뜻. "which makes it well suited to streaming"
favor~에 유리하다, ~을 선호하게 만들다 · 어떤 구조가 특정 상황에 더 적합하다는 뜻. "favors large static text"
turn X into YX를 Y로 바꾸다 · 매번 스캔하던 것을 한 번 전처리로 바꾼다는 뜻. "preprocess the text once and answer queries"
operationally운영상으로, 실제 운용 측면에서 · 인덱스를 한 번 만드는 편이 안정적이라는 뜻. "operationally far more stable"
memory footprint메모리 점유량 · 자료구조가 차지하는 메모리 크기. "compact memory footprint favors large"
DAWG접미사 오토마톤(Deterministic Acyclic Word Graph) · 문자열의 모든 부분 문자열을 인식하는 최소 상태 기계. "A suffix automaton (DAWG) encodes the same information as a state machine"
LCP최장 공통 접두사(Longest Common Prefix) · 인접한 접미사 사이의 공통 접두사 길이를 저장하는 배열. "pairing it with an LCP array"

If you study this on a given day, add a note link and a ✅ to this line in the source curriculum (docs/knowledge/dev-100-curriculum.md) and this spot will lead straight to the note body. You can also write directly on this page — but regenerating overwrites it, so it's safer to keep anything you want to save as markdown under docs/algorithms/.


한국어

문자열 인덱스 TODO

Algorithms · Day 9 / 100 · A. 고급 알고리즘·자료구조 (Day 1–19)

서픽스 배열·오토마톤 (로그·트레이스 검색)

개념

서픽스 배열은 문자열의 모든 접미사를 사전순으로 정렬한 뒤 그 시작 위치만 배열로 보관하는 인덱스이다. 정렬돼 있으므로 임의의 패턴 검색을 이분 탐색으로 처리할 수 있고, 인접한 접미사의 공통 접두사 길이를 담은 LCP 배열을 함께 두면 반복 부분문자열, 서로 다른 부분문자열 개수 같은 질의도 선형에 가깝게 풀린다. 서픽스 오토마톤(DAWG)은 같은 정보를 상태 기계로 표현한 것으로, 문자열의 모든 부분문자열을 인식하는 최소 결정적 오토마톤이며 상태 수가 입력 길이에 선형으로 유지된다. 오토마톤은 온라인으로 한 글자씩 추가하며 만들 수 있어 스트리밍에 유리하고, 서픽스 배열은 메모리가 조밀해 대용량 정적 텍스트에 유리하다. 둘 다 "패턴을 매번 스캔한다"를 "텍스트를 한 번 전처리하고 질의는 상수·로그 시간에 답한다"로 바꾸는 것이 본질이다.

로그·트레이스처럼 텍스트는 고정돼 있는데 검색은 수없이 반복되는 상황에서, grep식 선형 스캔은 데이터가 커지는 순간 그대로 비용이 된다.

코드 · 수식

# Day 9: 문자열 인덱스 — 서픽스 배열로 부분 문자열 검색을 이분 탐색으로 처리
# 모든 접미사를 정렬해 배열로 두면, 패턴 검색이 선형 스캔 대신 O(log n · m)에 끝난다.

def build_suffix_array(text):
    return sorted(range(len(text)), key=lambda i: text[i:])

def search(text, sa, pattern):
    lo, hi = 0, len(sa)
    while lo < hi:
        mid = (lo + hi) // 2
        if text[sa[mid]:sa[mid] + len(pattern)] < pattern:
            lo = mid + 1
        else:
            hi = mid
    if lo == len(sa) or text[sa[lo]:sa[lo] + len(pattern)] != pattern:
        return []
    hits, i = [sa[lo]], lo + 1
    while i < len(sa) and text[sa[i]:sa[i] + len(pattern)] == pattern:
        hits.append(sa[i]); i += 1
    return sorted(hits)

text = "the quick brown fox jumps over the lazy dog the fox runs"
sa = build_suffix_array(text)
print("서픽스 배열(앞 10개 시작 위치) =", sa[:10])
for pattern in ["fox", "the", "cat"]:
    print(f"search('{pattern}') -> 위치 {search(text, sa, pattern)}")

연습

10MB짜리 로그 파일에 대해 서픽스 배열 + LCP를 만들고, 동일한 부분문자열 질의 1000개를 단순 스캔과 이분 탐색으로 각각 돌려 시간을 비교해 보기.

실무 · Verex 연결

노드 로그나 트랜잭션 트레이스에서 특정 주소·셀렉터·이벤트 시그니처를 반복 검색할 때, 인덱스를 한 번 세워 두는 쪽이 매번 전체 스캔하는 것보다 운영상 훨씬 안정적이다.

Jayverse에서의 위치

핵심 표현

이 페이지의 영어 본문에서 배울 만한 단어와 표현, 뜻과 나온 자리.

Expression뜻 · 쓰이는 자리
lexicographically사전순으로 · 접미사들을 알파벳 순서로 정렬한다는 뜻. "sorts all suffixes of a string lexicographically"
well suited to~에 잘 맞는, 적합한 · 온라인 빌드 방식이 스트리밍에 알맞다는 뜻. "which makes it well suited to streaming"
favor~에 유리하다, ~을 선호하게 만들다 · 어떤 구조가 특정 상황에 더 적합하다는 뜻. "favors large static text"
turn X into YX를 Y로 바꾸다 · 매번 스캔하던 것을 한 번 전처리로 바꾼다는 뜻. "preprocess the text once and answer queries"
operationally운영상으로, 실제 운용 측면에서 · 인덱스를 한 번 만드는 편이 안정적이라는 뜻. "operationally far more stable"
memory footprint메모리 점유량 · 자료구조가 차지하는 메모리 크기. "compact memory footprint favors large"
DAWG접미사 오토마톤(Deterministic Acyclic Word Graph) · 문자열의 모든 부분 문자열을 인식하는 최소 상태 기계. "A suffix automaton (DAWG) encodes the same information as a state machine"
LCP최장 공통 접두사(Longest Common Prefix) · 인접한 접미사 사이의 공통 접두사 길이를 저장하는 배열. "pairing it with an LCP array"

공부한 날 원본 커리큘럼(docs/knowledge/dev-100-curriculum.md)의 이 줄에 노트 링크와 ✅ 를 붙이면, 이 자리는 노트 본문으로 바로 이어집니다. 노트 없이 이 페이지에 바로 적어도 됩니다 — 다만 다시 생성하면 덮어쓰이므로, 남길 글은 docs/algorithms/ 의 마크다운으로 쓰는 편이 안전합니다.

← 1061. 순서 통계1063. 세그먼트 트리 심화 →