본문으로 바로가기
제품데스크톱 도구TASTECAD도면 확인TASTEFILES파일 관리TASTEFTP파일 전송TASTESSH원격 작업공간TASTEZIP압축과 해제개발 도구TASTECODERust 통합 개발 환경TASTESTUDIO통합 개발·테스트 환경TASTEETL데이터 통합(ETL)서버TASTEREMOTE원격 데스크톱 서버TASTEGEO공간정보 서버TASTEMAIL메일 작업공간시작하기다운로드문서구매모든 제품
마켓플레이스
소식
개발소식오늘의 개발 소식개발 문서프로그래밍 언어RustTypeScriptJavaScriptPythonGoC#JavaCC++KotlinDart프런트엔드 프레임워크Next.jsReactVue.js · NuxtSvelteKitAngular서버 프레임워크Spring BootDjangoFastAPIASP.NET CoreNestJS · Express앱 · 데스크톱 프레임워크TauriFlutterReact NativeElectron알고리즘알고리즘 기초알고리즘 핵심알고리즘 심화실무 알고리즘
커뮤니티프로젝트개발 로그프롬프트AI 도구 이슈쇼케이스커뮤니티 안내
협업기술 전수홈페이지 구축데스크톱 앱 구축릴리스·결제 파이프라인판매 파트너문의
서비스원격 데스크톱설치 패키지 커스텀
TASTEDEV
제품

데스크톱 도구

TASTECAD복잡한 도면을 가볍게 열다TASTEFILES필요한 파일만, 가장 빠르게TASTEFTP파일이 오가는 가장 단정한 길TASTESSH서버와 나 사이, 더 단단한 연결TASTEZIP더 빠르게 묶고, 더 안전하게 풀다

개발 도구

TASTECODERust 앱을 편집 · 빌드 · 디버깅 · 테스트까지 한 창에서TASTESTUDIO편집부터 원격 테스트와 승인 기반 AI 수정까지TASTEETL값을 보며 데이터를 옮깁니다

서버

TASTEREMOTE우리 회사 서버로 운영하는 원격 데스크톱TASTEGEOGeoServer 호환 공간정보 서버를 Rust로 다시 세웁니다TASTEMAIL메일의 주도권을 다시 내 손에

시작하기

다운로드모든 제품 최신 판문서제품 사용법구매구독 · 영구 라이선스모든 제품모든 제품 한눈에
모든 제품
마켓플레이스소식
개발

소식

오늘의 개발 소식언어 · 프레임워크 · GitHub · AI 공식 발표개발 문서언어 · 프레임워크 · 알고리즘 안내서

프로그래밍 언어

RustTypeScriptJavaScriptPythonGoC#JavaCC++KotlinDart

프레임워크

프런트엔드 프레임워크Next.jsReactVue.js · NuxtSvelteKitAngular
서버 프레임워크Spring BootDjangoFastAPIASP.NET CoreNestJS · Express
앱 · 데스크톱 프레임워크TauriFlutterReact NativeElectron

알고리즘

알고리즘 기초복잡도 분석 · 정렬 · 탐색 · 기본 자료구조 · 트리와 힙알고리즘 핵심그래프 탐색 · 최단 경로와 최소 신장 트리 · 동적 계획법 · 탐욕 알고리즘 · 분할 정복 · 백트래킹알고리즘 심화문자열 알고리즘 · 수학과 정수론 · 비트 연산 · 구간 트리 · 유니온 파인드실무 알고리즘코딩 면접 풀이 전략 · LRU 캐시 · 레이트 리미터 · 일관된 해시
개발 문서 전체 보기
커뮤니티
프로젝트빌더와 작업 발견개발 로그결정과 시행착오프롬프트재사용 가능한 지시AI 도구 이슈Claude · Codex · Gemini 문제와 우회법쇼케이스완성된 결과커뮤니티 안내공개하고 함께 만드는 방식
협업
기술 전수Rust·egui 네이티브 앱 만들기홈페이지 구축10개 언어, 계정, 문서·다운로드데스크톱 앱 구축Rust·egui, WebView 없이릴리스·결제 파이프라인서명 · MSI·deb·rpm·AppImage · 해시판매 파트너소개하고 결제액의 30% 받기문의구축 · 제휴 · 투자 문의
서비스
원격 데스크톱브라우저로 내 컴퓨터에 접속설치 패키지 커스텀필요한 기능만, 설정은 미리
구매
KO
한국어EnglishDeutschEspañolFrançaisItalianoPortuguês (Brasil)日本語简体中文繁體中文
로그인
로그인
TASTEDEV

작은 도구부터, 더 좋은 개발까지.

support@tastedev.netTASTEDEV 후원하기
제품제품군구매문서다운로드마켓플레이스고객 지원
커뮤니티프로젝트개발 로그프롬프트AI 도구 이슈쇼케이스
정책개인정보처리방침이용약관구독 약관환불 정책커뮤니티 운영 정책
계정로그인가입하기
방문자
오늘
012345678901234567890123456789012345678901234567890123456789
어제
012345678901234567890123456789012345678901234567890123456789
누적 조회
0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
상호
지엑스소프트
대표자
최영구
사업자등록번호
853-44-00368
주소
인천광역시 미추홀구 경인로 351 9층 V24
전화
+82-10-4404-1523
이메일
whitecalvin@tastedev.net
개인정보 보호책임자
최영구

© 2026 TASTEDEV제품 정보는 현재 검증된 개발 소스를 기준으로 합니다.출시·고도화 중

문자열 알고리즘 문서
문자열 알고리즘
개념동작 원리구현복잡도연습 문제실무 활용
토론

문자열 알고리즘 안내서 · 3/6

구현

KMP 검색과 트라이를 Python으로 구현해 설명하고, 같은 KMP 핵심 루틴을 C++, Java, TypeScript로 옮깁니다.

Python: KMP

def prefix_function(p: str) -> list[int]:
    pi = [0] * len(p)                    # pi[i]: p[0..i]의 가장 긴 경계 길이
    k = 0                                # 직전 위치의 경계 길이
    for i in range(1, len(p)):
        while k > 0 and p[i] != p[k]:    # 늘릴 수 없으면 더 짧은 경계로
            k = pi[k - 1]
        if p[i] == p[k]:                 # 한 글자 늘릴 수 있으면 늘린다
            k += 1
        pi[i] = k
    return pi


def kmp_search(text: str, pattern: str) -> list[int]:
    m = len(pattern)
    if m == 0:
        return list(range(len(text) + 1))
    pi = prefix_function(pattern)
    hits, k = [], 0                         # k: 지금까지 일치한 길이
    for i, ch in enumerate(text):
        while k > 0 and ch != pattern[k]:
            k = pi[k - 1]
        if ch == pattern[k]:
            k += 1
        if k == m:
            hits.append(i - m + 1)
            k = pi[k - 1]                   # 겹치는 출현도 찾도록 이어 간다
    return hits


assert prefix_function("ababaca") == [0, 0, 1, 2, 3, 0, 1]
assert kmp_search("abababcabab", "abab") == [0, 2, 7]
assert kmp_search("aaaa", "aa") == [0, 1, 2]
  • while은 실패할 때 k를 줄이고, if는 일치할 때 한 칸 늘립니다. 두 함수의 뼈대가 같습니다.
  • 빈 패턴은 str.find처럼 모든 위치에서 일치한다고 정했습니다. 이 처리가 없으면 pattern[0]에서 인덱스 오류가 납니다.
  • i - m + 1은 패턴이 끝난 위치 i에서 거꾸로 계산한 시작 위치입니다.

Python: 트라이와 자동 완성

class TrieNode:
    def __init__(self) -> None:
        self.children: dict[str, "TrieNode"] = {}
        self.is_end = False


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

    def insert(self, word: str) -> None:
        node = self.root
        for ch in word:
            node = node.children.setdefault(ch, TrieNode())
        node.is_end = True

    def _walk(self, prefix: str) -> TrieNode | None:
        node = self.root
        for ch in prefix:
            node = node.children.get(ch)
            if node is None:
                return None
        return node

    def complete(self, prefix: str, limit: int = 10) -> list[str]:
        start = self._walk(prefix)
        if start is None:
            return []
        out: list[str] = []
        stack = [(start, prefix)]
        while stack and len(out) < limit:
            node, word = stack.pop()
            if node.is_end:
                out.append(word)
            for ch in sorted(node.children, reverse=True):
                stack.append((node.children[ch], word + ch))
        return out


t = Trie()
for w in ["car", "cart", "cat", "dog"]:
    t.insert(w)
assert t.complete("ca") == ["car", "cart", "cat"]
  • 자식은 dict로 두어 어떤 알파벳에도 쓸 수 있습니다. 소문자만 쓴다면 길이 26 배열이 더 빠릅니다.
  • is_end가 없으면 접두사일 뿐인 ca도 단어로 나옵니다.
  • complete는 재귀 대신 명시적 스택을 씁니다. 자식을 역순으로 넣으므로 사전순으로 꺼내지고, limit개를 모으면 멈춥니다.

C++

#include <string>
#include <vector>

std::vector<int> prefixFunction(const std::string& p) {
    std::vector<int> pi(p.size(), 0);
    int k = 0;
    for (size_t i = 1; i < p.size(); ++i) {
        while (k > 0 && p[i] != p[k]) k = pi[k - 1];
        if (p[i] == p[k]) ++k;
        pi[i] = k;
    }
    return pi;
}

std::vector<int> kmpSearch(const std::string& text, const std::string& pattern) {
    std::vector<int> hits;
    const int m = static_cast<int>(pattern.size());
    if (m == 0) return hits;
    const std::vector<int> pi = prefixFunction(pattern);
    int k = 0;
    for (size_t i = 0; i < text.size(); ++i) {
        while (k > 0 && text[i] != pattern[k]) k = pi[k - 1];
        if (text[i] == pattern[k]) ++k;
        if (k == m) {
            hits.push_back(static_cast<int>(i) - m + 1);
            k = pi[k - 1];
        }
    }
    return hits;
}

std::string은 바이트 단위이므로 UTF-8 텍스트에서는 바이트 위치를 돌려줍니다. 빈 패턴은 빈 결과로 단순화했습니다.

Java

import java.util.ArrayList;
import java.util.List;

public final class Kmp {
    static int[] prefixFunction(String p) {
        int[] pi = new int[p.length()];
        int k = 0;
        for (int i = 1; i < p.length(); i++) {
            while (k > 0 && p.charAt(i) != p.charAt(k)) k = pi[k - 1];
            if (p.charAt(i) == p.charAt(k)) k++;
            pi[i] = k;
        }
        return pi;
    }

    static List<Integer> search(String text, String pattern) {
        List<Integer> hits = new ArrayList<>();
        int m = pattern.length();
        if (m == 0) return hits;
        int[] pi = prefixFunction(pattern);
        int k = 0;
        for (int i = 0; i < text.length(); i++) {
            while (k > 0 && text.charAt(i) != pattern.charAt(k)) k = pi[k - 1];
            if (text.charAt(i) == pattern.charAt(k)) k++;
            if (k == m) {
                hits.add(i - m + 1);
                k = pi[k - 1];
            }
        }
        return hits;
    }
}

Java의 charAt은 UTF-16 코드 단위를 돌려줍니다. 텍스트와 패턴을 같은 방식으로 나누므로 일치 판정은 정확하지만, 위치는 코드 단위 기준입니다.

TypeScript

export function prefixFunction(p: string): number[] {
  const pi = new Array<number>(p.length).fill(0);
  let k = 0;
  for (let i = 1; i < p.length; i++) {
    while (k > 0 && p[i] !== p[k]) k = pi[k - 1];
    if (p[i] === p[k]) k++;
    pi[i] = k;
  }
  return pi;
}

export function kmpSearch(text: string, pattern: string): number[] {
  const hits: number[] = [];
  const m = pattern.length;
  if (m === 0) return hits;
  const pi = prefixFunction(pattern);
  let k = 0;
  for (let i = 0; i < text.length; i++) {
    while (k > 0 && text[i] !== pattern[k]) k = pi[k - 1];
    if (text[i] === pattern[k]) k++;
    if (k === m) {
      hits.push(i - m + 1);
      k = pi[k - 1];
    }
  }
  return hits;
}

정리

  • KMP의 두 함수는 "실패하면 줄이고, 일치하면 늘린다"는 같은 뼈대를 씁니다.
  • 트라이는 노드마다 자식 맵과 is_end 표시를 두며, 자동 완성은 접두사 노드에서 아래를 훑습니다.
  • 위치 단위는 C++이 바이트, Java와 TypeScript가 UTF-16 코드 단위, Python이 코드 포인트입니다.
이 장에 대해 질문이 있나요?
이전 장동작 원리다음 장복잡도

댓글 0개

인기 댓글순최신순

로그인 · 로그인하면 댓글을 남길 수 있습니다.