목차
1. 트라이(Trie)란?
2. 트라이(Trie) 이해하기
3. 트라이(Trie)의 단점
4. 트라이(Trie) 접목하기
5. 트라이(Trie) 문제 풀어보기
트라이(Trie)란?
문자열에 특화된 자료 구조인 트라이(Trie)는 문자열 집합을 표현하는 트리 자료구조이며,
우리가 원하는 원소를 찾는 작업을 O(n)에 해결 할 수 있는 자료구조이다.
이제 트라이에 대해 이해해보는 시간을 가져 보자.
그리고 다음 질문에 대해 한번 생각해보자.
map을 이용하면 충분히 트라이와 같은 효과를 낼 수 있지 않을까??
트라이(Trie) 이해하기
이번에는 그림을 통해 트라이를 이해해보고자 한다.
이러한 트라이를 생성해두면, 결국 원하는 단어(원소)를 찾을 때 O(m)의 시간만에 찾아 낼 수 있게 된다.
(이때 m은 문자열 길이를 의미한다.)
이제 처음에 이야기한 내용에 대해 살펴보자.
map을 이용하면 충분히 트라이와 같은 효과를 낼 수 있지 않을까??
트라이(Trie)의 단점
위의 AC는 트라이이고, 아래 AC는 map을 이용하였다.
확실히 메모리와 시간에서 서로가 차이가 나고 있음을 확인할 수 있다.
위의 문제에 대한 소스 코드를 보고 소스 코드를 분석해보자.
소스 코드 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | #include <iostream> #include <cstring> #include <algorithm> using namespace std; const int TrieNode = 10; // 트라이 노드마다 포인터 개수 struct Trie { Trie *next[TrieNode]; // 다음 노드를 가리키는 포인터 배열 bool finish; // 이 노드에서 끝나는 전화번호가 있는가? bool nextChild; // 이 노드의 자식이 있는가? // 생성자 Trie() { fill(next, next + TrieNode, nullptr); finish = nextChild = false; } // 소멸자 ~Trie() { for (int i = 0; i < TrieNode; i++) if (next[i]) delete next[i]; } // 문자열 key를 현재 노드부터 삽입. 삽입 후 일관성이 있는지를 리턴 bool insert(const char *key) { // 문자열의 끝이라면 if (*key == '\0') { finish = true; // 문자열이 끝났는데도 // 자식이 있다면 일관성이 없다. if (nextChild) return 0; else return 1; } int nextKey = *key - '0'; // 다음으로 가는 트라이가 없다면 if (!next[nextKey]) next[nextKey] = new Trie; nextChild = true; // 여기까지 왔다는 의미는 트라이의 자식이 분명히 있다는것. // 이때 자식에서 일관성이 없던게 밝혀지거나 // finish가 true라면 일관성이 없다는 의미. bool get = next[nextKey]->insert(key + 1); return !finish && get; } }; int main() { int tc; scanf("%d", &tc); while (tc--) { int n; scanf("%d", &n); // 트라이의 루트 생성 Trie *root = new Trie; bool chk = true; for (int i = 0; i < n; i++) { char phone[11]; scanf("%s", phone); // 일관성이 없다면 if (chk && root->insert(phone) == 0) chk = false; } if (chk) printf("YES\n"); else printf("NO\n"); // 트라이 소멸 delete root; } return 0; } // This source code Copyright belongs to Crocus // If you want to see more? click here >> | Crocus |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | const int TrieNode = 10; // 트라이 노드마다 포인터 개수 struct Trie { Trie *next[TrieNode]; // 다음 노드를 가리키는 포인터 배열 bool finish; // 이 노드에서 끝나는 전화번호가 있는가? bool nextChild; // 이 노드의 자식이 있는가? // 생성자 Trie() { fill(next, next + TrieNode, nullptr); finish = nextChild = false; } // 소멸자 ~Trie() { for (int i = 0; i < TrieNode; i++) if (next[i]) delete next[i]; } |
트라이의 첫번째 부분이다.
TrieNode는 알파벳 혹은 숫자같은 트라이에서 필요로 하는 포인터의 개수이다.
Trie *next[TrieNode]; 는 트라이가 생성될 때 같이 생성되는 포인터 배열을 의미한다.
bool finish는 이 지점에서 끝나는 문자열이 존재하는지 확인해주는 변수이다.
bool nextChild는 이 노드의 자식이 있는지 확인하는 변수이다.
그리고 생성자에서는 모든 포인터를 nullptr로 초기화, finish, nextChild는 false로 초기화한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | // 문자열 key를 현재 노드부터 삽입. 삽입 후 일관성이 있는지를 리턴 bool insert(const char *key) { // 문자열의 끝이라면 if (*key == '\0') { finish = true; // 문자열이 끝났는데도 // 자식이 있다면 일관성이 없다. if (nextChild) return 0; else return 1; } int nextKey = *key - '0'; // 다음으로 가는 트라이가 없다면 if (!next[nextKey]) next[nextKey] = new Trie; nextChild = true; // 여기까지 왔다는 의미는 트라이의 자식이 분명히 있다는것. // 이때 자식에서 일관성이 없던게 밝혀지거나 // finish가 true라면 일관성이 없다는 의미. bool get = next[nextKey]->insert(key + 1); return !finish && get; } |
이제 트라이에서 가장 중요한 insert부분이다.
문자열의 끝이라 '\0'를 만난다면 finish = true가 될 것이다.
이때 이 문제에서는 일관성을 요구하였기에 finish가 true인데 nextChild가 존재한다면
her이 이미 있는데 he가 들어왔을때를 의미하는 것과 같은 것이므로 일관성이 없다는 것이다.
(문제에서는 선영의 번호가 91 12 54 26인데 긴급전화가 911이라 일관성이 없다고 하는 것과 같다.)
그리고 nextKey는 *key -'0'로 나타낼 수 있고
현재 nextKey가 있는데 다음으로 가는 포인터 배열이 존재하지 않는다면 트라이를 하나 새로 만들어야 한다.
그리고 nextChild는 true가 될 것이다.
이제 insert 이후 return을 받게 될텐데 이때 일관성이 없다고 판단되었거나,
자식이 존재하는데 finish가 true였던 경우에는 또 일관성이 없다고 return해주는 방식으로 진행한다.
트라이(Trie) 문제 풀어보기
[14425번] 문자열 집합 :: https://www.acmicpc.net/problem/14425
[5052번] 전화번호 목록 :: https://www.acmicpc.net/problem/5052
[4358번] 생태학 :: https://www.acmicpc.net/problem/4358
[5670번] 휴대폰 자판 :: https://www.acmicpc.net/problem/5670
[3080번] 아름다운 이름 :: https://www.acmicpc.net/problem/3080
[5446번] 용량 부족 :: https://www.acmicpc.net/problem/5446
[13505번] 두 수 XOR :: https://www.acmicpc.net/problem/13505
'Applied > 자료구조' 카테고리의 다른 글
[STL] Pair 구현 (0) | 2018.01.26 |
---|---|
[STL] Vector 구현 (0) | 2018.01.26 |
SCC(Strongly Connected Component) (0) | 2017.07.21 |
우선 순위 큐 소스 코드 (0) | 2017.03.24 |
유니온 파인드(Union Find, Disjoint Set) (16) | 2017.03.23 |