2 분 소요

전화번호 목록 (백준 Gold 4)

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

전화번호 목록이 주어지고
그 번호들이 일관성이 있는지 없는지를 구하는 문제

  • 일관성이 있으려면
    ‘특정한 번호’가 ‘다른 번호’의 접두어가 아니여야 함

ex)
911, 976 이라는 번호가 주어졌는데
9114 라는 번호가 주어지면 911까지 누른 순간
실패처리

접근 방식

각 문자들에 대한 ‘트라이’를 구현함으로서
풀 수 있겠다고 느꼈다

Image

(출처 : 나무위키(트라이))

트라이는
트리 구조의 응용으로서
‘각 단어’의 해당하는 부분을 ‘다음 단어’와 연결시킴으로서
‘단어’ 자체를 트리 방식으로 저장하는 자료구조이다

따라서
9 -> 1 -> 1(끝)
이런식으로 문자를 저장하고

차후에 들어오는 문자열이
9 -> 1 -> 1 (이미 끝인데? 실패처리!)

하는 것이 이번 문제에 대한 접근방식이다

첫 제출 코드

#include<iostream>
#include<vector>
#include<map>

using namespace std;

class node
{
public:
	node()
	{
		isEnd = false;
	}

	bool isInsert(string& str,int idx)
	{
		if (isEnd)
			return false;

		char c = str[idx];
		int sSize = str.size();
		
		if (next.find(c) == next.end())
		{
			next[c].now = c;
			if (idx == sSize - 1)
			{
				next[c].isEnd = true;
				return true;
			}
		}

		return next[c].isInsert(str, idx + 1);
	}
private:
	bool isEnd;
	char now;
	map<char, node> next;
};

int main()
{
	int t;
	cin >> t;
	while (t > 0)
	{
		int n;
		cin >> n;
		vector<string> strs(n);
		for (int i = 0; i < n; i++)
			cin >> strs[i];

		node root;
		bool isSuccess = true;
		
		for (string& str : strs)
		{
			if (root.isInsert(str,0) == false)
			{
				isSuccess = false;
				break;
			}
		}

		if (isSuccess)
			cout << "YES" << '\n';
		else
			cout << "NO" << '\n';
		t--;
	}

	return 0;
}

틀린 이유

자꾸 메모리 초과가 발생하거나
세그먼트 falut 에러가 발생하였다

수정 방법

  • 메모리 초과는 node를 *로 잡아 new 할당을 해주는 것으로 수정해보았다

  • 세그먼트 falut는 처음에 vector(10001)로 잡아두었지만
    반복문을 여전히 (for auto) 방식으로 돌고 있기에 해당 부분을 수정

  • 또한 ‘길이가 긴’ 코드가 먼저 들어오는 경우
    현재의 tree insert 방식으로는
    idx가 런타임 에러를 발생할 수 있기에
    sort를 통하여 사전순 정렬을 하며
    동시에 길이가 짧은 문자열이 먼저 들어오도록 수정하였다

최종 제출 코드

#include<iostream>
#include<vector>
#include<unordered_map>
#include<algorithm>

using namespace std;

class node
{
public:
	node()
	{
		isEnd = false;
	}

	~node()
	{
		for (auto& p : next)
		{
			delete p.second;
		}
	}

	bool isInsert(string& str, int idx)
	{
		if (isEnd)
			return false;

		char c = str[idx];
		int sSize = str.size();
		// 정렬 안하면 길이가
		// 높은것이 먼저 들어와 터질 수 있음
		if (next.find(c) == next.end())
		{
			next[c] = new node();
			next[c]->now = c;
			if (idx == sSize - 1)
			{
				
				next[c]->isEnd = true;
				return true;
			}
		}

		return next[c]->isInsert(str, idx + 1);
	}
private:
	bool isEnd;
	char now;
	unordered_map<char, node*> next;
};

int main()
{
	int t;
	cin >> t;
	
	while (t > 0)
	{
		int n;
		cin >> n;
		vector<string> strs(n);
		for (int i = 0; i < n; i++)
			cin >> strs[i];

		// 정렬함으로서, 같은 사전순이라도 길이 짧은게 앞으로 오도록 한다
		sort(strs.begin(), strs.end());

		node root;
		bool isSuccess = true;

		for (int i = 0; i < n;i++)
		{
			if (root.isInsert(strs[i], 0) == false)
			{
				isSuccess = false;
				break;
			}
		}

		if (isSuccess)
			cout << "YES" << '\n';
		else
			cout << "NO" << '\n';

		t--;
	}

	return 0;
}

결과

Image

트라이를 오랜만에 구현해보았고
길이가 높은것이 먼저들어올 경우를 생각하지 못하여
해당 부분에 대하여 다소 삽질을 하였다

댓글남기기