세로 조작
가로 조작과 상관 없이 A가 아니면 무조건 해줘야하는 조작이므로 우선 계산해준다.
char c = name.charAt(i);
answer += Math.min(c - 'A', 'Z' - c + 1);
'A'에서 위로 조작하여 변경하는 방법과 아래로 조작하여 'Z'로 돌아가서 변경하는 방법 중 적게 조작하는 방법을 더해준다.
가로 조작
가로 조작 부분은 구현도 까다롭고, 처음엔 이해하기도 어려웠다.
처음에는 커서의 현재 위치에서 왼쪽과 오른쪽 중 더 가까운 'A'가 아닌 문자를 찾아 이동하고, 그 위치에서 다시 같은 과정을 반복하는 방식으로 구현했는데, 이 방법은 성능이 좋지 않아 테스트 통과율이 51.9 / 100에 그쳤다.
그래서 다른 사람들의 풀이를 참고하게 되었고,
그 과정에서 가로 조작의 핵심은 'A'가 연속된 구간을 얼마나 잘 피하느냐에 있다는 것을 이해하게 되었다.
핵심 로직
'조작할 필요 없는 A'가 연속된 구간은 커서 이동을 건너뛸 수 있는 후보다.
따라서 문자열을 왼쪽부터 탐색하면서 A가 길게 이어진 구간을 찾고,
이 구간을 기준으로 앞쪽을 먼저 처리할지, 뒤쪽을 먼저 처리할지를 비교하여
더 짧은 커서 이동 경로를 선택하면 된다.
예시: "ABAABBA"
이 문자열을 예로 들면, 인덱스 1에 있는 B 왼쪽에 A가 두 개 연속으로 존재한다.
그 이후에도 다시 B가 나오는데, 이처럼 A 구간을 기준으로 앞과 뒤를 나눌 수 있다.
앞쪽을 먼저 처리하는 경우
move = Math.min(move, i + i + len - next);
뒤쪽을 먼저 처리하는 경우
move = Math.min(move, (len - next) * 2 + i);
위 두 방법을 비교하며 더 작은 값을 구하면 된다.
class Solution {
public int solution(String name) {
int answer = 0;
int len = name.length();
int move = len - 1; // 최악의 경우: 한쪽 끝까지 쭉 이동
for (int i = 0; i < len; i++) {
// 세로 조작 (알파벳 변경)
char c = name.charAt(i);
answer += Math.min(c - 'A', 'Z' - c + 1);
// 가로 조작 (커서 이동 최소화 계산)
int next = i + 1;
while (next < len && name.charAt(next) == 'A') {
next++;
}
// i에서 왼쪽으로 갔다가 다시 우회해서 오는 경우 계산
move = Math.min(move, i + i + len - next);
// 또는 오른쪽 끝까지 갔다가 왼쪽으로 오는 경우
move = Math.min(move, (len - next) * 2 + i);
}
answer += move;
return answer;
}
}