조이스틱(Java)

bearMin·2024년 2월 27일

🎯문제

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

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

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

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

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

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

제한 사항

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

입출력 예

namereturn
"JEROEN"56
"JAN"23

✏️풀이

코드

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', 'Z' - name.charAt(i) + 1);
            
            // 다음 위치를 지정
            int index = i + 1;
            // 연속된 A의 개수를 저장
            while(index < name.length() && name.charAt(index) == 'A') {
                index++;
            }
            
            // 앞쪽으로 진행했다가 뒤돌아가는 것과 비교
            move = Math.min(move, i * 2 + name.length() - index);
            // 뒤쪽부터 진행하고 다시 앞으로 오는 것과 비교
            move = Math.min(move, (name.length() - index) * 2 + i);
        }
        
        return answer + move;
    }
}

설명

그리디를 사용하는 문제였다.

우선 move라는 변수에 처음부터 쭉 진행했을 경우의 값을 저장해준다.
그다음 반복문을 진행하면서 answer 변수에 알파벳을 순서대로 세는 것과 반대로 세는 것 중 최솟값을 선택해서 더해준다.

예를 들어, name.charAt(i) = 'W'라고 했을 때 name.charAt(i) - 'A'는 22가 되고 'Z' - name.charAt(i) + 1은 4가 된다. 즉, 알파벳을 순서대로 세는 것보다 끝에서부터 세는 것이 더 빠르고 그 값은 4가 된다는 것을 알 수 있다.

index 변수에 다음 위치를 지정해준 뒤에
while문을 통해 연속된 A의 개수를 저장한다.
연속된 A의 개수를 저장해주는 이유는 진행 방향을 확인하기 위해서이다.

현재 move에는 순서대로 진행했을 경우 최솟값이 저장되어있다. 하지만 A가 연속되어 나올 경우 앞쪽으로 진행했다가 되돌아가는 경우나 뒤쪽 먼저 진행하고 다시 앞으로 오는 경우가 더 빠를 수도 있다.

  1. 앞쪽으로 진행했다가 뒤돌아가가는 것이 더 빠른 경우의 예는 JDAAAAAAACBB,
  2. 뒤쪽으로 진행했다가 앞으로 되돌아가는 것이 더 빠른 경우의 예는 JSAAAAAAAB 이다.

1번의 경우 먼저 맨 앞의 위치를 바꾸고 맨 뒤로 커서를 이동해서 값을 바꿔주면 순서대로 진행하는 것보다 빠르게 만들 수 있다.
연속된 A의 시작 위치는 i+1이고 끝나는 위치는 index이다. 현재 i까지 진행했다고 하면, 수식으로 나타낼 때 i 2 + name.length() - index가 된다.
i
2를 해주는 이유는 1번의 예시에서 JD까지 갔다가 다시 뒤쪽으로 커서를 돌리는 경우처럼 되돌아가는 경우를 구해주기 위해서이다.

2번의 경우 맨 뒤쪽을 먼저 바꾼 뒤에 맨 앞으로 다시 와서 값을 바꿔주면 순서대로 진행하는 것보다 빠르게 만들 수 있다.
연속된 A의 시작 위치는 i+1이고 끝나는 위치는 index이다. 현재 i까지 진행했다고 하면, 수식으로 나타낼 때 (name.length() - index) 2 + i가 된다.
(name.length() - index)
2를 해주는 이유는 1번에서 얘기했던 것처럼 맨뒤로 커서를 옮긴 뒤에 다시 되돌아가서 맨 앞으로 커서를 옮겨주기 위해서이다.

이처럼 위의 과정을 반복한 뒤에 나온 answer와 move의 값을 더해서 반환해주면 문제를 해결할 수 있다!


💡느낀 점

어려웠다. 알파벳을 순서대로 세는 것과 반대로 세는 것까지는 쉽게 생각을 했으나 좌우 이동에서 많은 시간이 걸렸고 여러 반례들을 찾아보면서 해결할 수 있었다. 생각한 것보다 조금 더 다양한 경우의 수들이 존재했고 모든 케이스를 만족하기 위해서 공책에 직접 적어보면서 풀었다. 적어보면서 느낀 것은 역시 뭐든 손으로 직접 해봐야 이해가 쉽게 되는구나를 알았다 :) 겨우겨우 이해해서 문제를 해결할 수 있었다..


링크

문제 링크

profile
소소한 공부기록

0개의 댓글