알고리즘/프로그래머스
[프로그래머스] 조이스틱_JAVA
jimkwon
2022. 2. 28. 14:10
반응형
문제 링크
https://programmers.co.kr/learn/courses/30/lessons/42860
먼저 조이스틱의 상, 하 조작을 살펴보자.
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;
}
}
좌우 커서 값을 찾는게 핵심인 문제였다!
코드가 이해가 안된다면 예시를 대입하여 과정을 따라가보는 것이 도움이 될 것이다.