조이스틱 문제는 프로그래머스 코딩테스트 연습 Greedy에 있습니다.
https://programmers.co.kr/learn/courses/30/lessons/42860


저는 개발자 지망생으로 공부하고 있는 대학생입니다.
때문에 아직 많이 부족합니다.

아래의 코드가 해당 문제를 풀 수 있는 코드는 맞지만, 해당 문제를 풀기위한 가장 좋은 코드는 아닙니다.
코드에 문제가 있거나, 부족한 점이 있다면 댓글 달아주시면 감사히 배우겠습니다.
감사합니다.

PS.

해당문제는 현재 2022년 1월 14일 지문 수정 및 테스트케이스가 추가로 인하여
기존 블로그 및 다른사람의 풀이를 보아도, 정답이 코드가 많은 문제가 있습니다.


조이스틱 문제

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

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

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

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

  1. 첫 번째 위치에서 조이스틱을 위로 9번 조작하여 J를 완성합니다.
  2. 조이스틱을 왼쪽으로 1번 조작하여 커서를 마지막 문자 위치로 이동시킵니다.
  3. 마지막 위치에서 조이스틱을 아래로 1번 조작하여 Z를 완성합니다.

따라서 11번 이동시켜 "JAZ"를 만들 수 있고, 이때가 최소 이동입니다.
만들고자 하는 이름 name이 매개변수로 주어질 때, 이름에 대해 조이스틱 조작 횟수의 최솟값을 return 하도록 solution 함수를 만드세요.

제한 사항

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

즉 최소한의 조이스틱 조작으로 해당 이름을 만드는 방법에 대한 문제입니다!

어릴적 오락실에서 게임이 끝난 이후 랭킹입력할 때를 생각하시면 될 것 같습니다.

주어지는 데이터와 return해야하는 값은 아래와 같습니다.

namereturn
"JEROEN"56
"JAN"23

자세한 문제의 내용은 프로그래머스에서 확인하실 수 있습니다.



전체 코드


우선 제 전체 코드입니다.

class Solution {
    public int solution(String name) {
        int answer = 0;
        int length = name.length();

        int index; // 다음 값들을 확인할 때 사용
        int move = length - 1; // 좌우 움직임 수를 체크

        for(int i = 0; i < name.length(); i++){
            answer += Math.min(name.charAt(i) - 'A', 'Z' - name.charAt(i) + 1);

            index = i + 1;
            // 연속되는 A 갯수 확인
            while(index < length && name.charAt(index) == 'A'){
                index++;
            }

            // 순서대로 가는 것과, 뒤로 돌아가는 것 중 이동수가 적은 것을 선택
            move = Math.min(move, i * 2 + length - index);
            // 2022년 이전 테스트 케이스만 확인하면 여기까지해도 정답처리가 되기 때문에, 이전 정답들에는 여기까지만 정리되어 있지만,
            // BBBBAAAAAAAB 와 같이, 처음부터 뒷부분을 먼저 입력하는 것이 더 빠른 경우까지 고려하려면 아래의 코드가 필요합니다.
            move = Math.min(move, (length - index) * 2 + i);
        }
        return answer + move;
    }
}

풀이 방법


제가 풀이한 방법은 상하로 이동하는 횟수와, 좌우로 이동하는 횟수를 따로 계산하였고,
연속되는 A의 갯수를 확인하는 것으로 언제 뒤로 돌아가는 것이 좋은지를 판단하였습니다.

1. 상하로 움직이는 부분을 더하는 부분 입니다.

이렇게 해결한 이유는, 가장 짧은 코드이기 때문입니다.
빠른 속도를 위해서는 13번째 값인 N을 기준으로 if문을 활용하여 계산하셔도 좋을 것 같습니다.

for(int i = 0; i < name.length(); i++){
	answer += Math.min(name.charAt(i) - 'A', 'Z' - name.charAt(i) + 1);

2. 연속되는 A의 갯수를 확인하는 부분입니다.

A의 연속되는 갯수를 기반으로 A기준 뒷부분의 길이와, A기준 앞부분의 길이를 판단할 수 있습니다.
이 길이를 기반으로 앞쪽, 뒷쪽 어디가 더 빠른지를 계산할 수 있습니다.

index = i + 1;
// 연속되는 A 갯수 확인
while(index < length && name.charAt(index) == 'A'){
	index++;
}

3. 이동 수가 적은 방법을 선택하는 부분입니다.

이부분에서 아마 다른 이전 블로그의 내용들과 다를 것 같은데

맨 뒷부분에 move = Math.min(move,(length - index) * 2 +i);는 연속된 A의 앞쪽보다 뒷쪽이 짧은 경우, 뒷쪽부터 조작하는 것이 더 적은 이동횟수를 가져갈 수 있기 때문에 이러한 부분을 추가하였습니다.

// 순서대로 가는 것과, 뒤로 돌아가는 것 중 이동수가 적은 것을 선택
move = Math.min(move, i * 2 + length - index);
// 2022년 이전 테스트 케이스만 확인하면 여기까지해도 정답처리가 되기 때문에, 이전 정답들에는 여기까지만 정리되어 있지만,
// BBBBAAAAAAAB 와 같이, 처음부터 뒷부분을 먼저 입력하는 것이 더 빠른 경우까지 고려하려면 아래의 코드가 필요합니다.
move = Math.min(move, (length - index) * 2 + i);
profile
Computer System을 공부하는 대학원생 입니다.

2개의 댓글

comment-user-thumbnail
2022년 8월 19일

덕분에 좋은 내용 잘 보고 갑니다
감사합니다.

답글 달기
comment-user-thumbnail
2023년 10월 26일

BABB처럼 뒤로만 이동하는 경우는 고려하지 않아도 되는건가요?

답글 달기