이번 문제는 너무 어려워서 결국엔 답지를 봤다.
하지만 답지를 봐도 소스가 무슨 소린지 이해를 못해 꽤나 오랜 시간을 소요 했다.
아무래도 내가 배열의 index를 가지고 활용하는 부분에 약한것 같다.
일단 문제부터 보자.
문제 설명
조이스틱으로 알파벳 이름을 완성하세요. 맨 처음엔 A로만 이루어져 있습니다.
ex) 완성해야 하는 이름이 세 글자면 AAA, 네 글자면 AAAA
조이스틱을 각 방향으로 움직이면 아래와 같습니다.
▲ - 다음 알파벳 ▼ - 이전 알파벳 (A에서 아래쪽으로 이동하면 Z로) ◀ - 커서를 왼쪽으로 이동 (첫 번째 위치에서 왼쪽으로 이동하면 마지막 문자에 커서) ▶ - 커서를 오른쪽으로 이동
예를 들어 아래의 방법으로 "JAZ"를 만들 수 있습니다.
- 첫 번째 위치에서 조이스틱을 위로 9번 조작하여 J를 완성합니다. - 조이스틱을 왼쪽으로 1번 조작하여 커서를 마지막 문자 위치로 이동시킵니다. - 마지막 위치에서 조이스틱을 아래로 1번 조작하여 Z를 완성합니다. 따라서 11번 이동시켜 "JAZ"를 만들 수 있고, 이때가 최소 이동입니다.
만들고자 하는 이름 name이 매개변수로 주어질 때, 이름에 대해 조이스틱 조작 횟수의 최솟값을 return 하도록 solution 함수를 만드세요.
제한 사항
- name은 알파벳 대문자로만 이루어져 있습니다.
- name의 길이는 1 이상 20 이하입니다.
입출력 예
name | return |
"JEROEN" | 56 |
"JAN" | 23 |
이 문제의 핵심은 일단 알파벳의 최소이동도 답이지만 제일 중요한점은 커서를 최소 이동시키는 부분이다.
나도 알파벳의 최소이동까지는 스스로 풀었지만 커서이동에서 막혔다.
아무래도 이부분은 내가 문제를 잘못 접근했던것 같다.
진짜 문제가 말하는대로 한번 실행을 해봤다면 문제를 풀었을지 모르겠다는 생각을 해봤다.
일단 문제에서는 어떤 이름을 커서이동해서 알파벳을 변경하는거다.
그림을 통해서 한번 설명을 해주겠다.
아래와 같은 이름이 있다고 가정을 해보자.
위와 같은 이름을 받았을때 우리는 A로 된 이름을 아래와 왼쪽에서 오른쪽으로 변경을 할수가 있다.
하지만 연속적으로 A가 나온 부분은 굳이 변경하지 않아도 되기때문에 A가 아닌 부분에서는 역주행을 해서 A가
아닌 부분만 바꿔주면 된다.
여기서 탐욕 알고리즘이 적용되는 부분이다.
탐욕 알고리즘은 매 순간 최선의 선택을 하는것인대
우리는 왼쪽에서 오른쪽으로 정주행하는것과 다시 역주행해서 맨뒤로 이동해서 연속되는 부분이전까지 이동한것중
더 작게 이동한것을 선택하는부분이 바로 탐욕 알고리즘이 적용 되는 부분이다.
그래서 알고리즘은 아래와 같다.
처음엔 정주행 했을때의 이동수를 min_move로 값을 초기화하 한다.
int min_move = len - 1;
그리고 그 다음부턴 이름의 값에 하나하나 접근 ( i ) 하면서
A가 있는 부분전( next )까지 갔다가 역주행해서 남은부분 과 정주행( min_move )으로 이동하는것과
비교해서 작은걸 고르는것이다.
그림으로 설명하자면 아래와 같다.
int len = name.length();
for(int i=0;i<len;i++)
일때
i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
위 그림을 소스로 보면 아래와 같다.
for(int i=0;i<len;i++){
System.out.println(name+"중 "+i+"번째 : "+ name.charAt(i));
int differ = Math.min(name.charAt(i)-'A','Z'-name.charAt(i)+1);
System.out.println("A부터"+name.charAt(i)+"까지 최소 이동 : "+ differ);
answer += differ;
//좌우: 연속된 A의 등장에 따라 최소 움직임이 달라짐
int next = i+1; //현재위치 다음부터
// 내 다음이 A라면 계속 NEXT++
while(next<len && name.charAt(next) == 'A'){//다음이 len보다 짧고 해당 부분이 'A'라면 진입
//여기서는 A인지 확인하는 부분.
System.out.println(name+"중 연속 A는" + next+"번째 까지 : "+name.charAt(next));
next++;
}
//min_move와 i+len-next+i중 작은 수를 리턴
System.out.println("i : " + i);
System.out.println("len : "+len);
System.out.println("next : "+next);
System.out.println("len-next(A를 제외하고 남은 이동) : "+ (len-next));
System.out.println("i+len-next+i : " + (i+len-next+i));
min_move = Math.min(min_move, i+len-next+i);
System.out.println("min_move : "+min_move);
System.out.println("answer : "+ answer);
System.out.println();
}
전체 코드는 아래와 같다.
public class PreTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
PreTest p = new PreTest();
String name1 = "JEROEN";
String name2 = "BAABBAABA";
System.out.println(p.solution(name2));
}
public int solution(String name) {
int answer = 0;
int len = name.length();
//최대로 가질수 있는 min 값은 끝까지 가는것
//길이 -1을 함 즉 마지막 index, 그래서 끝까지 가는것
int min_move = len - 1;
for(int i=0;i<len;i++){
System.out.println(name+"중 "+i+"번째 : "+ name.charAt(i));
int differ = Math.min(name.charAt(i)-'A','Z'-name.charAt(i)+1);
System.out.println("A부터"+name.charAt(i)+"까지 최소 이동 : "+ differ);
answer += differ;
//좌우: 연속된 A의 등장에 따라 최소 움직임이 달라짐
int next = i+1; //현재위치 다음부터
// 내 다음이 A라면 계속 NEXT++
while(next<len && name.charAt(next) == 'A'){//다음이 len보다 짧고 해당 부분이 'A'라면 진입
//여기서는 연속 A확인하는 부분인거 같음
System.out.println(name+"중 연속 A는" + next+"번째 까지 : "+name.charAt(next));
next++;
}
//min_move와 i+len-next+i중 작은 수를 리턴
System.out.println("i : " + i);
System.out.println("len : "+len);
System.out.println("next : "+next);
System.out.println("len-next(앞으로 남은 이동) : "+ (len-next));
System.out.println("i+len-next+i : " + (i+len-next+i));
min_move = Math.min(min_move, i+len-next+i);
System.out.println("min_move : "+min_move);
System.out.println("answer : "+ answer);
System.out.println();
}
answer+=min_move;
return answer;
}
}
느낀점
1. 문제가 말하는대로 한번직접 해보자.
2. 소스를 보고 이해가 안갈땐 디버깅을 해보고 디버깅을 해도 이해가 안되면 손코딩을 해봐라
3. 보자마자 무슨 알고리즘을 짜야할지 먼저 생각을 해보자.
4. 여러 테스트케이스를 생각을 해보자.
'코딩일기 > 알고리즘' 카테고리의 다른 글
[프로그래머스 : 레벨2] 구명보트 : 탐욕알고리즘(Java) (0) | 2021.06.13 |
---|---|
[프로그래머스 : 레벨2] 큰 수 만들기 :Stack (Java) (0) | 2021.06.11 |
[프로그래머스 : 레벨 1] 체육복 (Java) (0) | 2021.06.09 |
[프로그래머스 : 레벨 2] 카펫 (java) (0) | 2021.06.08 |
[프로그래머스 : 레벨2] 소수 찾기 : 재귀호출 (Java) (0) | 2021.06.08 |