[프로그래머스] Lv.2 영어 끝말잇기 / JavaScript

이희령·2023년 10월 15일
0

알고리즘

목록 보기
2/20
post-thumbnail

문제 링크

문제 설명

1부터 n까지 번호가 붙어있는 n명의 사람이 영어 끝말잇기를 하고 있습니다. 영어 끝말잇기는 다음과 같은 규칙으로 진행됩니다.

  1. 1번부터 번호 순서대로 한 사람씩 차례대로 단어를 말합니다.
  2. 마지막 사람이 단어를 말한 다음에는 다시 1번부터 시작합니다.
  3. 앞사람이 말한 단어의 마지막 문자로 시작하는 단어를 말해야 합니다.
  4. 이전에 등장했던 단어는 사용할 수 없습니다.
  5. 한 글자인 단어는 인정되지 않습니다.

다음은 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
3["tank", "kick", "know", "wheel", "land", "dream", "mother", "robot", "tank"][3,3]
5["hello", "observe", "effect", "take", "either", "recognize", "encourage", "ensure", "establish", "hang", "gather", "refer", "reference", "estimate", "executive"][0,0]
2["hello", "one", "even", "never", "now", "world", "draw"][1,3]

입출력 예 #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를 말했기 때문에 이때 처음 탈락자가 나오게 됩니다.

나의 풀이

1차 풀이

function solution(n, words) {
    for (let i = 0; i <= words.length; i++) {
        if (i === 0) continue; // 첫 번째 단어인 경우 바로 다음 인덱스 순회함
        if (i === words.length) return [0, 0]; // 마지막 단어인 경우 탈락자가 생기지 않았음을 의미
        
        const prevWord = words[i - 1]; // 이전 단어
        const nextWord = words[i]; // 현재 단어
        
        const wrongWord = prevWord[prevWord.length - 1] !== nextWord[0]; // 끝말잇기에 실패했을 경우
        const sameWord = words.indexOf(nextWord) !== i; // 동일한 단어를 말했을 경우
        
        if (wrongWord || sameWord) {
            const currentWordOrder = i + 1;
            const value = currentWordOrder % n;
            
            const eliminationNumber = value ? value : currentWordOrder / n;
            const eliminationOrder = Math.ceil(currentWordOrder / n); 
            
            return [eliminationNumber, eliminationOrder]
        }
    }
}

테스트 케이스 4문제를 통과하지 못했다. 원인을 찾기 위해 내가 놓친 조건이 있나 문제를 다시 꼼꼼하게 읽어봤다.

2차 풀이

function solution(n, words) {
    for (let i = 0; i < words.length; i++) {
        if (i === 0) continue; // 첫 번째 단어인 경우 바로 다음 인덱스 순회함
        
        const prevWord = words[i - 1]; // 이전 단어
        const nextWord = words[i]; // 현재 단어
        
        const wrongWord = prevWord[prevWord.length - 1] !== nextWord[0]; // 끝말잇기에 실패했을 경우
        const sameWord = words.indexOf(nextWord) !== i; // 동일한 단어를 말했을 경우
        
        if (wrongWord || sameWord) {
            const currentWordOrder = i + 1;
            const value = currentWordOrder % n;
            
            const eliminationNumber = value ? value : currentWordOrder / n;
            const eliminationOrder = Math.ceil(currentWordOrder / n); 
            
            return [eliminationNumber, eliminationOrder]
        }
    }
    
    return [0,0] // 끝말잇기에 성공했을 경우
}

마지막 인덱스인 경우에 마지막 단어가 끝말잇기에 실패했는지 여부를 판단하지 않고 바로 [0, 0]를 return하기 때문에 여기서 문제가 생길 수 있다고 판단하고 return문을 for문 바깥으로 이동하는 방식으로 풀이를 수정했다. 하지만 변함없이 4개의 테스트 케이스를 통과하지 못하고 있다.

최종 풀이

function solution(n, words) {
    for (let i = 0; i < words.length; i++) {
        if (i === 0) continue; // 첫 번째 단어인 경우 바로 다음 인덱스 순회
        
        const prevWord = words[i - 1]; // 이전 단어
        const nextWord = words[i]; // 현재 단어
        
        const wrongWord = prevWord[prevWord.length - 1] !== nextWord[0]; // 끝말잇기에 실패했을 경우
        const sameWord = words.indexOf(nextWord) !== i; // 동일한 단어를 말했을 경우
        
        if (wrongWord || sameWord) {
            return [i % n + 1, Math.floor(i / n) + 1];
        }
    }
    
    return [0,0] // 끝말잇기에 성공했을 경우
}

끝말잇기에 실패했을 경우에 if문에서 배열을 return하는 부분을 전체적으로 수정했다. 기존에는 if문 안에서 변수도 많이 선언하고 삼항연산자도 사용했었는데 return문 한 줄로 수정하여 더욱 가독성 좋은 코드를 만들었다.

  1. for문을 이용해 words 배열의 각 단어를 순회한다.
  2. 첫 번째 단어인 경우에는 continue를 사용하여 바로 다음 인덱스로 이동한다.
  3. 문자열의 인덱스로 접근하는 방법을 이용하여 앞사람이 말한 단어의 마지막 문자로 시작하는 단어를 말하지 못한 경우를 wrongWord 변수에 boolean 형태를 할당한다.
  4. 배열에서 주어진 요소를 찾을 수 있는 "첫 번째 인덱스"를 반환하는 indexOf 메서드를 이용하여 배열 안에서 해당 단어를 가진 첫 번째 인덱스와 현재 인덱스가 다를 경우를 boolean 형태로 sameWord 변수에 할당한다.
  5. if문을 이용해 wrongWordsameWord 둘 중에 하나가 true인 경우, 가장 먼저 탈락하는 사람의 번호와 그 사람이 자신의 몇 번째 차례에 탈락했는지를 담은 배열을 return한다.
  6. 만약 for문을 모두 순회하고도 wrongWord || sameWord if문에 해당하지 않았다면 탈락자가 발생하지 않은 것이므로 [0,0]를 return한다.

다른 사람의 풀이

function solution(n, words) {
    let answer = 0;
    words.reduce((prev, now, idx) => {
        answer = answer || ((words.slice(0, idx).indexOf(now) !== -1 || prev !== now[0]) ? idx : answer);
        return now[now.length-1];
    }, "")

    return answer ? [answer%n+1, Math.floor(answer/n)+1] : [0,0];
}

reduce 메서드의 특성을 활용한 재미있는 풀이 방법이었다. 끝말잇기에 실패하는 경우에는 해당 indexanswer에 할당한다. 그리고 prev 누적값에는 다음 단어에서 끝말잇기에 성공했는지를 판단하는데 이용하기 위해 현재 단어의 마지막 글자를 할당한다.

근데 해당 풀이에서는 끝말잇기에 실패하더라도 reduce 함수를 종료하지 않고 모든 요소를 순회하기 때문에 효율성 측면에서 좋은 답변인지는 잘 모르겠다.

profile
Small Steps make a Big Difference.🚶🏻‍♀️

0개의 댓글