Computer Science/알고리즘(Java)

[알고리즘] 프로그래머스 - 조이스틱(Java)

웅지니어링 2022. 12. 21. 01:41

코딩테스트 연습 - 조이스틱 | 프로그래머스 스쿨 (programmers.co.kr)

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

* 문제 설명

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

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

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

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

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

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

* 제한사항

  • name은 알파벳 대문자로만 이루어져 있습니다.
  • name의 길이는 1 이상 20 이하입니다.
입출력 예
name = "JEROEN" return = 56
name = "JAN" return = 23

 

* 풀이

조이스틱의 조작법은 방향에 따라 2가지로 나눌 수가 있다. 상하조작과 좌우조작이다. 따라서 상하조작과 좌우조작으로 나눠서 생각하면 조금 수월하게 풀 수 있다. 알파벳의 종류는 26가지이고, 어느 경우에 조작 횟수가 최소가 되는지 구하면 되는 그리디 알고리즘 문제라고 할 수 있다. 필자는 상하조작의 경우에는 쉽게 생각을 하였으나, 좌우조작의 경우에는 쉽사리 생각을 하지 못하였다.따라서 머리를 엄청 굴렸고 좌우조작은 경우에 따라 코딩을 짜야 한다는 결론이 나왔다. 먼저 상하조작부터 생각을 해보자. 상하조작의 경우, 알파벳 A에서 위로 조작했을 경우(A -> B -> C....), 아래로 조작했을 경우를 따져봐야 한다. (A -> Z -> Y -> X....) 따라서 반복문을 통해 위로 조작했을 경우와 아래로 조작했을 경우의 수 중 어느 것이 최소인지 따지면 된다. (answer += Math.min(name.charAt(i) - 'A', 'Z' - name.charAt(i) + 1);) 다음은 좌우조작이다. 좌우 조작의 경우에는 3가지로 나눌 수가 있다. "AAAAAA" 같은 문자열의 경우에는 처음부터 끝까지(-> 방향으로) 탐색을 해봐야 한다.(경우의 수 1.) "BBAAAAACCC" 같은 문자열의 경우에는 오른쪽으로 갔다가(A를 만날 때까지 -> 방향으로 ) 왼쪽으로(B에서 C로 가면서 A를 만날 때까지 <-) 방향을 틀어야 한다.(경우의 수 2.) "BBBAAAAACC" 같은 문자열의 경우에는 왼쪽으로 갔다가(C부터 시작하여 A를 만날 때까지 <- 방향으로) 오른쪽으로(C에서 B로 가면서 A를 만날 때까지 ->) 방향을 틀어야 한다.(경우의 수 3.) 따라서 이를 코딩을 구현을 하면 풀이는 끝이다.(경우의 수1과 경우의 수2 비교,  경우의 수1과 경우의 수2 비교된 값과 경우의 수3 비교)

import java.util.*;

class Solution {
    public int solution(String name) {
        int answer = 0;
        int nameLength = name.length();
        int cursorIndex = 0;
        int moveCount = nameLength - 1;
        
        for(int i = 0; i < nameLength; i++) {
            answer += Math.min(name.charAt(i) - 'A', 'Z' - name.charAt(i) + 1);
            
            cursorIndex = i + 1;
            while(cursorIndex < nameLength && name.charAt(cursorIndex) == 'A') {
                cursorIndex++;
            }
            
            moveCount = Math.min(moveCount, i * 2 + nameLength - cursorIndex);
            moveCount = Math.min(moveCount, (nameLength - cursorIndex) * 2 + i);
        }
        
        return answer + moveCount;
    }
}