Coding test

[프로그래머스] 조이스틱 (Lv 2) - javascript (22.06 풀이)

Jiwoo 2022. 6. 19. 17:18

📝 문제

조이스틱으로 알파벳 이름을 완성하세요. 맨 처음엔 A로만 이루어져 있습니다.
ex) 완성해야 하는 이름이 세 글자면 AAA, 네 글자면 AAAA

조이스틱을 각 방향으로 움직이면 아래와 같습니다.

 

▲ - 다음 알파벳
▼ - 이전 알파벳 (A에서 아래쪽으로 이동하면 Z로)
◀ - 커서를 왼쪽으로 이동 (첫 번째 위치에서 왼쪽으로 이동하면 마지막 문자에 커서)
▶ - 커서를 오른쪽으로 이동 (마지막 위치에서 오른쪽으로 이동하면 첫 번째 문자에 커서)

 

예를 들어 아래의 방법으로 "JAZ"를 만들 수 있습니다.

 

- 첫 번째 위치에서 조이스틱을 위로 9번 조작하여 J를 완성합니다.
- 조이스틱을 왼쪽으로 1번 조작하여 커서를 마지막 문자 위치로 이동시킵니다.
- 마지막 위치에서 조이스틱을 아래로 1번 조작하여 Z를 완성합니다.
따라서 11번 이동시켜 "JAZ"를 만들 수 있고, 이때가 최소 이동입니다.

 

만들고자 하는 이름 name이 매개변수로 주어질 때, 이름에 대해 조이스틱 조작 횟수의 최솟값을 return 하도록 solution 함수를 만드세요.

 

제한 사항

  • name은 알파벳 대문자로만 이루어져 있습니다.
  • name의 길이는 1 이상 20 이하입니다.

 

🤔 나의 풀이

function solution(name) {

    let cnt = 0;
    let lgth = name.length;
    
    if(name.includes('A')) {
        let Arepeat = 0;
        let Aindex = name.indexOf('A');
        for(let i = Aindex + 1; i < lgth; i++) {
            if(name[i] === 'A') Arepeat ++;
            else break;
        }
        
        if(Arepeat+1 === lgth) return 0;
        
        if(Arepeat === 0) {
            cnt += Math.min(lgth -1, (Aindex-1)*2 + lgth - Aindex -1);
        }
        else {
            cnt += Math.min(lgth -1, (Aindex-1)*2 + (lgth - Aindex - Arepeat -1));
        }
    }
    
    else cnt += name.length - 1;
    
    for(let i = 0; i < lgth; i++) {

        if(name[i] === 'A') continue;
        
        let unicode = name.charCodeAt(i);
        
        if(unicode < 79) cnt += unicode - 65;
        else cnt += 90 - unicode + 1;
    }
    
    return cnt;
}

 

1. 일단 A를 찾음

1-1.있다면 연달아 있는지 체크 -> 연달아 있는 A를 제외하고 앞, 뒤 문자의 개수를 계산해서 앞으로 이동할지 뒤로 이동할지 결정

1-2. 연달아 있지 않다면 첫번째 A의 앞, 뒤 문자의 개수를 계산해서 이동 경로 계산

1-3. A가 없다면 앞에서 순서대로 이동

2. name을 순회하면서 A가 아닌 알파벳의 이동 횟수 더해줌

 

그러나 통과하지 못했다.

앞에서 출발, A 직전에 맨 앞으로 돌아와서 뒤로 A까지 가는 경로만 고려했는데

뒤에서 출발하는 같은 경로를 고려하지 못한 결과였다.

 

  • 정확도 테스트 결과

 

🔑 모범 풀이

function solution(name) {
    let answer = 0;
    let lgth = name.length;
    let upDownCnt = 0;
    let LRcnt = [lgth-1];
    
    for(let s of name){upDownCnt += upDown(s)};
    
    for(let i = 0; i < lgth; i++) {
        let nextIdx = i + 1;
        while(name[nextIdx] == 'A') nextIdx ++;
        LRcnt.push(i*2 + (lgth - nextIdx));
        LRcnt.push(i + (lgth - nextIdx)*2);
    }
    
    answer = upDownCnt + Math.min(...LRcnt);
    return answer;
    
    function upDown(temp) { 
        if(temp === 'A') return 0;
        let unicode = temp.charCodeAt();
        return unicode < 78 ? unicode - 65 : 91 - unicode;
    }
}

 

풀이 과정

이 문제에서 가장 어려운 것은 알파벳을 변경하기 위한 이동 경로를 정하는 부분이다.

위 풀이는 A를 찾은 다음, 경로를 구성하지 않는다.

순회하면서 해당 인덱스까지 왔다고 생각하고 다음 인덱스가 A인지 확인한 다음, 여러 경로를 저장해놓는다.

그 중 최소값이 올바른 경로가 되는 것이다.

 

★ 이동경로 설정 과정

  1. name을 순서대로 순회한다.
  2. 다음 인덱스가 A인지 확인한다. 만약 A라면 다음 인덱스는 변경이 불필요하니까 nextIndex에 1을 추가한다.
  3. 앞에서부터 현재 인덱스까지 왔다가, 다시 돌아가서, 뒤에서부터 nextIndex까지 가는 경우 (0 -> A.., 0 <- A.., ..A <- -1)
  4. 뒤에서부터 nextIndex까지 왔다가, 다시 돌아가서, 앞에서부터 현재 인덱스 전까지 오는 경우 (..A <- -1, ..A -> -1, 0 -> A..) 를 계산해서 LRcnt에 추가하기
  5. 이 중 최소값이 올바른 경로가 된다.
function solution(name) {

    let answer = 0;
    let lgth = name.length;
    let upDownCnt = 0; // 위,아래로 알파벳 변경할 횟수
    let LRcnt = [lgth-1]; // 왼,오로 이동할 경우의 수 모두 저장해서 최소값 가려낼 예정
    
    for(let s of name){upDownCnt += upDown(s)}; // name 순회해서 알파벳 변경 횟수 계산
    
    for(let i = 0; i < lgth; i++) { // ★
        let nextIdx = i + 1; 
        while(name[nextIdx] == 'A') nextIdx ++;
        LRcnt.push(i*2 + (lgth - nextIdx));
        LRcnt.push(i + (lgth - nextIdx)*2);
    }
    
    answer = upDownCnt + Math.min(...LRcnt);
    return answer;
    
    function upDown(temp) { // 알파벳 변경 함수
        if(temp === 'A') return 0; // A면 변경x
        let unicode = temp.charCodeAt(); // 아니라면 유니코드 확인
        return unicode < 78 ? unicode - 65 : 91 - unicode; // A부터 or Z부터 이동 여부 결정해서 return
    }
    
}

 

  • 정확도 테스트 결과


참고

programmers.co.kr/questions/27706

velog.io/@jeeseob5761/