프로그래머스 스쿨 - 영어 끝말잇기 Level2
https://school.programmers.co.kr/learn/courses/30/lessons/12981
프로그래머스
코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.
programmers.co.kr
문제 설명
1부터 n까지 번호가 붙어있는 n명의 사람이 영어 끝말잇기를 하고 있습니다. 영어 끝말잇기는 다음과 같은 규칙으로 진행됩니다.
- 1번부터 번호 순서대로 한 사람씩 차례대로 단어를 말합니다.
- 마지막 사람이 단어를 말한 다음에는 다시 1번부터 시작합니다.
- 앞사람이 말한 단어의 마지막 문자로 시작하는 단어를 말해야 합니다.
- 이전에 등장했던 단어는 사용할 수 없습니다.
- 한 글자인 단어는 인정되지 않습니다.
다음은 3명이 끝말잇기를 하는 상황을 나타냅니다.
tank → kick → know → wheel → land → dream → mother → robot → tank
위 끝말잇기는 다음과 같이 진행됩니다.
- 1번 사람이 자신의 첫 번째 차례에 tank를 말합니다.
- 2번 사람이 자신의 첫 번째 차례에 kick을 말합니다.
- 3번 사람이 자신의 첫 번째 차례에 know를 말합니다.
- 1번 사람이 자신의 두 번째 차례에 wheel을 말합니다.
- (계속 진행)
끝말잇기를 계속 진행해 나가다 보면, 3번 사람이 자신의 세 번째 차례에 말한 tank 라는 단어는 이전에 등장했던 단어이므로 탈락하게 됩니다.
사람의 수 n과 사람들이 순서대로 말한 단어 words 가 매개변수로 주어질 때, 가장 먼저 탈락하는 사람의 번호와 그 사람이 자신의 몇 번째 차례에 탈락하는지를 구해서 return 하도록 solution 함수를 완성해주세요.
- 끝말잇기에 참여하는 사람의 수 n은 2 이상 10 이하의 자연수입니다.
- words는 끝말잇기에 사용한 단어들이 순서대로 들어있는 배열이며, 길이는 n 이상 100 이하입니다.
- 단어의 길이는 2 이상 50 이하입니다.
- 모든 단어는 알파벳 소문자로만 이루어져 있습니다.
- 끝말잇기에 사용되는 단어의 뜻(의미)은 신경 쓰지 않으셔도 됩니다.
- 정답은 [ 번호, 차례 ] 형태로 return 해주세요.
- 만약 주어진 단어들로 탈락자가 생기지 않는다면, [0, 0]을 return 해주세요.
입출력 예nwordsresult입출력 예 설명

입출력 예 #1
3명의 사람이 끝말잇기에 참여하고 있습니다.
- 1번 사람 : tank, wheel, mother
- 2번 사람 : kick, land, robot
- 3번 사람 : know, dream, tank
와 같은 순서로 말을 하게 되며, 3번 사람이 자신의 세 번째 차례에 말한 tank라는 단어가 1번 사람이 자신의 첫 번째 차례에 말한 tank와 같으므로 3번 사람이 자신의 세 번째 차례로 말을 할 때 처음 탈락자가 나오게 됩니다.
입출력 예 #2
5명의 사람이 끝말잇기에 참여하고 있습니다.
- 1번 사람 : hello, recognize, gather
- 2번 사람 : observe, encourage, refer
- 3번 사람 : effect, ensure, reference
- 4번 사람 : take, establish, estimate
- 5번 사람 : either, hang, executive
와 같은 순서로 말을 하게 되며, 이 경우는 주어진 단어로만으로는 탈락자가 발생하지 않습니다. 따라서 [0, 0]을 return하면 됩니다.
입출력 예 #3
2명의 사람이 끝말잇기에 참여하고 있습니다.
- 1번 사람 : hello, even, now, draw
- 2번 사람 : one, never, world
와 같은 순서로 말을 하게 되며, 1번 사람이 자신의 세 번째 차례에 'r'로 시작하는 단어 대신, n으로 시작하는 now를 말했기 때문에 이때 처음 탈락자가 나오게 됩니다.
문제 해결
단순한 영어 끝말잇기 탈락자를 알아내는 문제이다. 끝말잇기는 중복이 허용되지 않으니 이 부분에서 map 자료구조를 사용해서 접근해보면 되겠다고 생각했다. map 자료구조는 키를 기반으로 데이터에 접근하며, 각 키는 유일해야 한다. map 자료구조를 이용해서 현재 나온 단어를 key(단어) value(나온 횟수)로 저장하여 현재 key값이 1이상이라면 중복된 key값이므로 바로 탈락자를 구분할 수 있다.
1. words 배열을 순회하면서 최초 나온 단어들을 map에 저장
2. 끝말잇기이므로 단어들의 마지막 문자는 저장하여 다음 단어의 첫문자와 비교
3. 만약 중복된 키값이 나온다면 탈락자 발생
- 끝말잇기가 몇 번 반복되었는지는 현재 반복 횟수를 전체 인원으로 나눠주면 된다.
- 끝말잇기를 틀린 사람의 순서는 현재 반복 횟수를 전체 인원으로 나눈 나머지를 구하면 된다.
std::map은 내부적으로 레드-블랙 트리(Red-Black Tree)라는 균형 이진 검색 트리를 사용하여 데이터를 저장한다. 이 트리는 데이터를 효율적으로 저장하고 검색하는 데 사용되며, 삽입, 삭제, 검색 연산 모두 평균적으로 O(log n)의 시간 복잡도를 갖는다. 이로 인해 std::map은 대부분의 상황에서 좋은 성능을 제공한다.
다음은 std::map의 주요 특징 및 사용법에 대한 간단한 설명이다.
- 키-값 쌍 저장: 각 원소는 키와 값으로 구성되어 있으며, 각 키는 유일해야 한다. 이를 통해 키를 기준으로 값을 검색하거나 수정할 수 있다.
- 자동 정렬: std::map은 레드-블랙 트리를 사용하기 때문에 원소들이 키의 순서대로 정렬된다. 이 특징은 범위 검색이나 특정 값보다 작은/큰 값을 찾는 데 유용하다.
- 삽입과 삭제: insert() 함수를 사용하여 새로운 키-값 쌍을 추가하거나, erase() 함수로 원소를 삭제할 수 있다.
- 검색: find() 함수를 사용하여 특정 키에 해당하는 값을 검색할 수 있다. 만약 해당 키가 존재하지 않으면 end()를 반환한다.
- 범위 검색: lower_bound()와 upper_bound() 함수를 사용하여 주어진 키 값 범위 내의 원소를 검색할 수 있다.
- 예외 처리: std::map은 레드-블랙 트리의 특성 상 모든 연산이 균형을 유지하며 동작하므로, 예외가 발생하지 않도록 설계되었다.
- 반복자 사용: begin()과 end() 함수를 사용하여 std::map의 원소를 순회할 수 있는 반복자를 얻을 수 있다.
- map[key] => 해당 방식으로 map의 접근하면 바로 그 key값에 값을 바꾸는 것이므로 초기화하는 경우가 아니면 사용하지 않는다.
- map.at(key) => key값으로 접근하고자 한다면 at을 이용하여 접근해야 한다.
- map.count(key) => 해당 방식으로 key값의 존재 여부를 확인할 수도 있다.
전체코드
#include <string>
#include <vector>
#include <map>
#include <iostream>
using namespace std;
typedef long long ll;
vector<int> solution(int n, vector<string> words) {
char lastWord = words[0][0];
vector<int>answer;
map<string, ll>m;
ll i = 0;
for(i=0; i<words.size(); i++){ // words를 순회
// 만약 끝말잇기 조건이 충족되면서 처음 들어온 단어라면 map에 저장
if(lastWord == words[i][0] and m.count(words[i]) == 0)
m.insert(make_pair(words[i], 1));
else // 아니라면 끝말잇기 종료
break;
lastWord = words[i][words[i].size()-1]; // 마지막 문자 저장
}
if(words.size()== i){ //정상종료된 경우
answer.push_back(0);
answer.push_back(0);
}
else{ //탈락자가 발생한 경우
answer.push_back((i%n) + 1);
answer.push_back((i/n) + 1);
}
return answer;
}