[프로그래머스] 조이스틱_JAVA

2022. 2. 28. 14:10알고리즘/프로그래머스

반응형

문제 링크

https://programmers.co.kr/learn/courses/30/lessons/42860

 

코딩테스트 연습 - 조이스틱

조이스틱으로 알파벳 이름을 완성하세요. 맨 처음엔 A로만 이루어져 있습니다. ex) 완성해야 하는 이름이 세 글자면 AAA, 네 글자면 AAAA 조이스틱을 각 방향으로 움직이면 아래와 같습니다. ▲ - 다

programmers.co.kr

 

먼저 조이스틱의 상, 하 조작을 살펴보자.

 

1. 조이스틱의 상,하 조작으로 원하는 알파벳 조작하기

조이스틱을 위로 조작해 알파벳을 찾는 경우는 '알파벳2' - '알파벳1'
처음 'A'에서 아래로 조작해 찾는 경우는 26(총 알파벳 수) - ('알파벳2' - '알파벳1')
ex) 'A' 'C' 의 경우 'C' - 'A' = 2로, 위로 두번 조작하면 가능하다. 
단,  'A' 'Y' 의 경우 'Y' - 'A' = 24보다 아래로 두번 조작하는 값(26 - 24)이 더 적다.
따라서 Math.min(알파벳2 - 알파벳1 , 26 - (알파벳2 - 알파벳1))로 최솟값을 구하여 적용하면 된다.

따라서 Math.min(알파벳2 - 알파벳1 , 26 - (알파벳2 - 알파벳1))로 최솟값을 구하여 적용하면 된다.

 

해당 경우는 크게 어렵지 않을 것이다. 이 문제의 핵심은 '좌우 조작'이다. 좌우 로직을 살펴보자

 

2. 조이스틱의 좌, 우 조작으로 커서 위치 조정하기

커서 위치가 바뀌게 되는 경우는 총 세 가지이다.
1. 처음부터 쭉 오른쪽으로 가는 경우
    가장 일반적인 경우로 name.length() - 1로 해당한다.
2. 오른쪽으로 갔다 다시 리턴하여 왼쪽으로 가는 경우
    ex) BBAAAAAYYY 의 경우, BB까지 갔다가 다시 돌아가 YYY를 완성해준다.
3. 왼쪽으로 갔다 다시 리턴하여 오른쪽으로 가는 경우
    ex) CCCAAAAAAY 의 경우, 처음부터 왼쪽으로 움직여 Y를 완성해주고 다시 오른쪽으로 돌아가 CCC를 완성해준다.

 

필자는 이 3번째 경우를 고려하지 못하여 난항을 겪었다.

'연속된 A' 여부에 따라 좌우 조작 방식이 바뀌게 되는 점을 착안하여 로직을 짰다.

 

1. name을 0부터 length()-1까지 name의 알파벳 검사를 한다.

2. 각 알파벳별로 상,하 조작을 구해준다.

3. 만약 내 다음 알파벳이 'A'라면, 'A'가 끝나는 지점을 구한다.

4. index == 0인 상태를 기준으로 (커서가 처음 위치하는 부분) 좌우조작의 2, 3번 경우 중 최솟값을 따져본다.

ex) 'ZYXAAAAAB'인 경우
현재 내 위치는 'X', index : 2 연속된 'A'가 끝나는 지점은 index : 8에 해당한다.
[1]   index : 0에서 'X'가 있는 2까지 왔다가 다시 0으로 돌아간 후, 왼쪽으로 더 움직여 B를 바꾸는 경우
       즉, ZYXYZB순으로 움직인 경우
       (i * 2) + (name.length() - 연속된 A가 끝나는 지점)
       여기서 i는 현재 내 위치, 즉 'X'의 index이다.
[2]  index : 0에서 왼쪽으로 움직였다 다시 0으로 온 후, 오른쪽으로 움직인 경우
       즉, ZBZYX순으로 움직인 경우
      (name.length() - 연속된 A가 끝나는 지점) * 2 + i

좌, 우 커서위치 조정하기의 2번, 3번에 해당하는 경우들을 위와 같이 공식으로 만들었다.

이제 다 왔다! 1번에 해당하는 name.length() - 1 값을 초기 좌우 커서 이동 횟수로 넣어두고,

2번과 3번의 로직과 비교하여 최솟값을 넣게 되면 끝!

// 알파벳은 총 26개, 두 알파벳 사이의 간격은  알파벳2 - 알파벳1 or 26 - (알파벳2 - 알파벳1)
// 위로 조작 : 해당하는 알파벳 - 'A'
// 아래로 조작 : 26 - 위로 조작한 횟수
// 1. A가 끝나는 지점을 endA변수에 구함. 그 지점까지 이동하는데 걸리는 값 왼, 오 비교
// 2. 더 짧은 방향을 move에 넣어줌. 가장 작은 move 마지막에 반환
import java.util.Arrays;
class Solution {
    public int solution(String name) {
        int answer = 0;
        int move = name.length() - 1; // 오른쪽으로 쭉 간 횟수
        
        for(int i = 0; i < name.length(); i++) {
            answer += Math.min(name.charAt(i) - 'A', 26 - (name.charAt(i) - 'A')); //상,하 알파벳 맞추기
            if (i < name.length() - 1 && name.charAt(i + 1) == 'A') {
                int endA = i + 1;
                while(endA < name.length() && name.charAt(endA) == 'A')
                    endA++;
                move = Math.min(move, i * 2 + (name.length() - endA));// 오른쪽으로 갔다 다시 왼쪽으로 꺾기
                move = Math.min(move, i + (name.length() - endA) * 2);// 왼쪽으로 갔다 다시 오른쪽으로 꺾기
            }
        }
        return answer + move;
    }
}

좌우 커서 값을 찾는게 핵심인 문제였다!

코드가 이해가 안된다면 예시를 대입하여 과정을 따라가보는 것이 도움이 될 것이다.