Search

Trie (트라이) 자료구조

트라이는 문자열을 저장하고 효율적으로 탐색하기 위한 트리형태의 자료구조입니다.
자동완성 기능, 사전 검색 등 문자열 탐색에 특화되어있습니다.
유용한 경우 예시)
문자열 리스트가 주어졋을 때 “ap”로 시작하는 서로 다른 문자열의 수 구하기
# 주어진 문자열 words = ["app", "apple", "apply", "apart", "ban", "banana"] # 찾고자 하는 문자열 (prefix) target = "ap"
Python
복사
 반복문 사용할 경우 (시간복잡도 O(문자개수×비교문자열길이)O(문자 개수 \times 비교 문자열 길이))
# 찾고자 하는 문자열의 길이 m = len(target) cnt = 0 for word in words: # prefix가 target과 일치하는 경우라면 # 수를 세어줍니다. if word[:m] == target: cnt += 1 print(cnt)
Python
복사
트라이를 사용하면 O(비교문자열길이)O(비교문자열길이)로 줄일 수 있습니다.
트라이는 다음과 같은 구조를 가진 트리입니다.
Root에서 시작하여 문자열 내 문자 순서대로 각 문자를 간선으로 하여 트리를 만들어나갑니다.
각 노드에 특정 문자열의 끝인지를 표시해두면 문제 풀이에 유용하게 사용할 수 있습니다.
ex) “app”과 “apple”을 순서대로 추가하였을 때
트라이를 만들기 위해서는 문자열 내 문자를 한 번씩만 순회해주면 되기 때문에 모든 문자열 길이 합을 L이라 했을 때 시간복잡도 O(L)O(L)로 구축할 수 있습니다.
Trie에 사용되는 노드를 TrieNode라는 클래스로 구현해보겠습니다. 각 노드는 자식 26개(”a” ~ “z”)를 관리하게 됩니다. (0 ~25의 인덱스 활용)
또한 문자열의 끝임을 표시하는 is_end라는 인스턴스 변수를 갖습니다.
class TrieNode(): def __init__(self): self.is_end = False self.children = [None] * 26
Python
복사
이 클래스를 통해 words내의 문자열들을 트라이 자료구조로 구축해보겠습니다.
words = ["app", "apple", "apply", "apart", "ban", "banana"] root = TriNode() def insert_word(s): t = root for char in s: # 문자열 내의 문자 순서대로 따라갑니다. index = ord(char) - ord("a") # 해당 노드가 없다면 새로운 노드 생성 if t.children[index] is None: t.children[index] = TrieNode() t = t.children[index] # 문자열을 추가한 마지막 위치 노드에 문자열의 끝임을 표시 t.is_end = True for word in words: insert_word(word)
Python
복사
구축한건 알겠는데, 그럼 이걸가지고 어떻게 빠르게 탐색할까요?
트라이 자료구조에서 “ap”의 위치를 찾는데에는 O(2), 즉 O(비교문자열길이)만큼이 필요합니다.
그럼 “ap”로 시작하는 서로 다른 문자열은 “ap”노드의 자손 중 is_end가 True인 노드의 개수입니다.
트리 노드의 수는 L개가 되기 때문에 O(L)의 시간을 들여 TreeDP를 통해 각 노드마다 자손 중 is_end인 자손이 몇개인지 미리 구해놓을 수 있습니다.
그러면 결국 “ap”로 시작하는 서로 다른 문자열 개수 찾기에는 O(2L)의 시간 복잡도가 필요하게 됩니다.