본문 바로가기

알고리즘 문제 풀이/Baekjoon

5670 - 휴대폰 자판 (Trie, 정적 풀이)

반응형
SMALL

https://www.acmicpc.net/problem/5670

 

5670번: 휴대폰 자판

문제 휴대폰에서 길이가 P인 영단어를 입력하려면 버튼을 P번 눌러야 한다. 그러나 시스템프로그래밍 연구실에 근무하는 승혁연구원은 사전을 사용해 이 입력을 더 빨리 할 수 있는 자판 모듈을 개발하였다. 이 모듈은 사전 내에서 가능한 다음 글자가 하나뿐이라면 그 글자를 버튼 입력 없이 자동으로 입력해 준다! 자세한 작동 과정을 설명하자면 다음과 같다. 모듈이 단어의 첫 번째 글자를 추론하지는 않는다. 즉, 사전의 모든 단어가 같은 알파벳으로 시작하더라도 반드

www.acmicpc.net

대놓고 트라이 써라는 문제입니다.

 

원래는 동적으로 필요할 때마다 만들어서 문제를 풀려고 했다가, 어차피 프로 시험에서 정적으로 문제 풀거 정적으로 풀어봐야겠다해서 정적으로 풀었습니다.

 

음...

 

다시 생각해보니 동적으로도 풀어봐야할거 같네요. 과연 시간 초과가 날지 궁금하니까요.

우선 정적으로 푼 코드를 살펴보기 전에, 알고리즘부터 타고 들어가봅시다.

 

문제는 읽어보시면 아실거고, 저희는 각 단어마다 언제 카운트를 해야하는지가 중요합니다.

 

만약 아래의 단어가 있다고 가정을 해봅시다.

발퀄 ㅈㅅ..

 

딱 봐도 아시겠죠? 트라이인거, 파란색 동그라미 쳐진 곳이 endOfWord라고 단어의 끝입니다.

 

즉, hi / hee / heeo / heeoy / heeoi 라는 단어들이 존재한다는 뜻입니다. (무슨 뜻인진 저도 모릅니다)

 

그렇다면 언제 카운트를 해줘야 할까요?

 

1. 맨 처음 => 맨 처음 단어가 어떻게 시작하는 지 알아야 가지를 펼칠 수 있겠죠.

2. n + 1 단어의 가지 수가 2개 이상일 때 => 2개 이상일 땐 어느 가지로 나아가야하는지 알아야 하니까 추가 입력이 필요합니다.

3. 한 단어가 endOfWord를 지나칠 때 => 위에서 보다시피 heeoy라는 단어로 가기 위해선 hee / heeo 라는 단어를 거치고 지나가야합니다. 따라서 추가적인 카운트를 그 때마다 해줘야 구분이 됩니다.

 

여기까지 분기하면 문제는 풀립니다.

 

Trie 알고리즘 설명은 다른 곳에서 많이 했으니 ㅎㅎ.. 제가 올려놓은 다른 곳에서 찾을 수 있으실 것입니다. ㅎㅅㅎ

https://ggodong.tistory.com/160

 

5052 - 전화번호 목록

https://www.acmicpc.net/problem/5052 5052번: 전화번호 목록 문제 전화번호 목록이 주어진다. 이때, 이 목록이 일관성이 있는지 없는지를 구하는 프로그램을 작성하시오. 전화번호 목록이 일관성을 유지하려면,..

ggodong.tistory.com

#include <stdio.h>
#include <string.h>

#define MAX_TRIE_LEN 1000000
#define ALPHABET 26

int trie[MAX_TRIE_LEN][ALPHABET];
int count[MAX_TRIE_LEN];
int endOfWord[MAX_TRIE_LEN];
int adr;

void initTrie() {
	adr = 0;
	memset(trie, 0, sizeof(trie));
	memset(count, 0, sizeof(count));
	memset(endOfWord, 0, sizeof(endOfWord));
}

void insert(char* word) {
	int idx = 0, num, cur = 0;
	while (word[idx] != '\0') {
		num = (int)word[idx] - 'a';
		if (!trie[cur][num]) {
			trie[cur][num] = ++adr;
			++count[cur];
		}
		cur = trie[cur][num];
		idx++;
	}
	endOfWord[cur] = 1;
}

double find(char* word) {
	int idx = 0, num, cur = 0;
	double cnt = 1;
	while (word[idx + 1] != '\0') {
		num = (int)word[idx] - 'a';
		cur = trie[cur][num];
		if (count[cur] != 1) {
			cnt++;
		}
		else if (endOfWord[cur]) cnt++;
		idx++;
	}
	return cnt;
}

int N;
char words[100000][81];
double res;

int main(void) {
	while (scanf("%d", &N) != EOF) {
		initTrie();
		res = 0;
		for (int n = 0; n < N; n++) {
			scanf("%s", words[n]);
			insert(words[n]);
		}
		for (int n = 0; n < N; n++) {
			res += find(words[n]);
		}
		res /= N;
		printf("%.2lf\n", res);
	}
	return 0;
}

 

 

사실 이 문제를 풀 때 제가 직면한 것은 알고리즘이 아니었습니다. (시도 횟수에서 아시겠죠)

 

아까 동적으로 풀려다가 정적으로 풀었다고 했는데, 아마 프로 문제에서 이 문제가 나왔다면 저는 통과할 수 없었을 것입니다.

 

그 이유는 initTrie()라는 함수 안에 있는데, 이 문제는 새로운 케이스가 들어올 때마다 새로 초기화를 해야하는 전역 변수 때문에 중간에 시간초과가 뜨게 되었습니다.

 

처음엔 for문으로 순회를 하면서 초기화를 하려했다가 도저히 시간을 뚫지 못했고, for 문보다 빠른게 memset 함수임을 알고 이를 사용해서 문제를 해결했습니다.

 

그게 좀 억울해서 동적으로 문제를 풀어야겠다 !!!!!!... 까지만 생각하고 밤이 너무 늦어 자려고 합니다.


이상 5670 - 휴대폰 자판 (Trie)였습니다. ^_^

반응형
LIST

'알고리즘 문제 풀이 > Baekjoon' 카테고리의 다른 글

5446 - 용량 부족 (Trie, 정적 풀이)  (0) 2020.02.11
13505 - 두 수 XOR (Trie)  (0) 2020.02.10
14425 - 문자열 집합 (Hash 풀이)  (0) 2020.02.07
14425 - 문자열 집합 (Trie 풀이)  (0) 2020.02.06
12791 - Starman  (0) 2020.02.04