코딩테스트/프로그래머스Programmers
[프로그래머스][Lv.1][JAVA]키패드 누르기
빔o0O
2024. 3. 27. 22:46
문제
스마트폰 전화 키패드의 각 칸에 다음과 같이 숫자들이 적혀 있습니다.
이 전화 키패드에서 왼손과 오른손의 엄지손가락만을 이용해서 숫자만을 입력하려고 합니다.
맨 처음 왼손 엄지손가락은 * 키패드에 오른손 엄지손가락은 # 키패드 위치에서 시작하며, 엄지손가락을 사용하는 규칙은 다음과 같습니다.
- 엄지손가락은 상하좌우 4가지 방향으로만 이동할 수 있으며 키패드 이동 한 칸은 거리로 1에 해당합니다.
- 왼쪽 열의 3개의 숫자 1, 4, 7을 입력할 때는 왼손 엄지손가락을 사용합니다.
- 오른쪽 열의 3개의 숫자 3, 6, 9를 입력할 때는 오른손 엄지손가락을 사용합니다.
- 가운데 열의 4개의 숫자 2, 5, 8, 0을 입력할 때는 두 엄지손가락의 현재 키패드의 위치에서 더 가까운 엄지손가락을 사용합니다.
4-1. 만약 두 엄지손가락의 거리가 같다면, 오른손잡이는 오른손 엄지손가락, 왼손잡이는 왼손 엄지손가락을 사용합니다.
순서대로 누를 번호가 담긴 배열 numbers, 왼손잡이인지 오른손잡이인 지를 나타내는 문자열 hand가 매개변수로 주어질 때, 각 번호를 누른 엄지손가락이 왼손인 지 오른손인 지를 나타내는 연속된 문자열 형태로 return 하도록 solution 함수를 완성해주세요.
[제한사항]
- numbers 배열의 크기는 1 이상 1,000 이하입니다.
- numbers 배열 원소의 값은 0 이상 9 이하인 정수입니다.
- hand는 "left" 또는 "right" 입니다.
- "left"는 왼손잡이, "right"는 오른손잡이를 의미합니다.
- 왼손 엄지손가락을 사용한 경우는 L, 오른손 엄지손가락을 사용한 경우는 R을 순서대로 이어붙여 문자열 형태로 return 해주세요.
(*입출력 예 생략)
분석
- 현재 각 손의 위치에 따라 눌러야 하는 키패드까지의 거리를 확인해야 하므로, 손의 위치를 계속 확인하고 업데이트해야 함.
- 왼쪽 열과 오른쪽 열은 누르는 손가락이 정해져있지만, 그렇다 해도 다음에 누를 키패드까지의 거리를 확인해야 하기 때문에 모든 키패드의 좌표를 알아야 한다.
피드백
- 처음에 numMap 을 초기화할 때 코드를 아래와 같이 사용했다. 그랬더니 0 을 키로 사용한 int[]의 인덱스 1의 값이 처음엔 1로 할당되었다가 이후에 0으로 변경되는 문제가 발생했다. 알고보니 배열을 재사용하여 numMap에 put 하는 과정에서 그것의 주소로 할당되기 때문에 *과 #의 좌표를 저장하면서 그 배열을 재활용해서 값을 다시 할당했더니 재할당한 값이 0을 키로 하는 값에도 그대로 반영되는 문제였다. 나름대로 메모리를 아꼈는데 아껴서 똥된다는게 이런 걸까.
Map<Integer, int[]> numMap = new HashMap();
int[] point = new int[2];
// 0 좌표 처리
point[0] = 3;
point[1] = 1;
numMap.put(0, point);
System.out.println(numMap.get(0)[0]);
System.out.println(numMap.get(0)[1]); // 여기선 1
// # 좌표 처리
point[0] = 3;
point[1] = 2;
numMap.put(35, point);
// * 좌표처리
point[0] = 3;
point[1] = 0;
numMap.put(42, point);
System.out.println(numMap.get(0)[0]);
System.out.println(numMap.get(0)[1]); // 여기선 0
2. 통과는 했지만 시간이 엄청 오래걸렸다. 이...이게 맞나? 일단 통과하고 다른 사람의 풀이를 봤는데, 아뿔싸. 내가 풀면서 아, numbers의 배열이 최대 1000개라면 String은 효율성이 떨어질 것 같은데, 라고 했던 부분이 정확히 맹점이었던... 그 부분을 고려했다면 StringBuilder 나 다른 클래스를 썼어야지!!! 이거를 고치고 나니까 시간효율이 엄청나게 좋아졌다. 젠장. 그래도 생각은 했다는 점에서 과거보다 많이 발전했다.
답안
import java.util.*;
class Solution {
public String solution(int[] numbers, String hand) {
StringBuilder answer = new StringBuilder();
int leftPoint = (char) '*'; // 왼손 위치
int rightPoint = (char) '#'; // 오른손 위치
Map<Integer, int[]> numMap = new HashMap();
// 1-9까지 키패드의 좌표를 map으로 삽입
for(int i = 1; i < 13; i++) {
int[] point = new int[2];
if(i < 10) {
point[0] = (i-1) / 3;
point[1] = (i % 3 == 0)? 2 : (i % 3) - 1;
numMap.put(i, point);
}else {
if(i == 10) {
// 0 좌표처리
point[0] = 3;
point[1] = 1;
numMap.put(0, point);
}else if(i == 11) {
// # 좌표 처리
point[0] = 3;
point[1] = 2;
numMap.put(35, point);
}else if(i == 12) {
// * 좌표처리
point[0] = 3;
point[1] = 0;
numMap.put(42, point);
}
}
}
for(int num : numbers) {
// 왼손을 쓰는 숫자 : 1, 4, 7
if(num % 3 == 1) {
answer.append("L");
leftPoint = num;
continue;
}else if(num != 0 && num % 3 == 0) {
answer.append("R");
rightPoint = num;
continue;
}
// 더 가까운 손 확인
int[] object = numMap.get(num);
// (1) 왼손
int[] nowHand = numMap.get(leftPoint);
int leftMove = Math.abs(object[0] - nowHand[0]) + Math.abs(object[1] - nowHand[1]);
// (2) 오른손
int[] nowHand2 = numMap.get(rightPoint);
int rightMove = Math.abs(object[0] - nowHand2[0]) + Math.abs(object[1] - nowHand2[1]);
if(leftMove == rightMove) {
if("left".equals(hand)) {
answer.append("L");
leftPoint = num;
}else {
answer.append("R");
rightPoint = num;
}
}else {
if(leftMove < rightMove) {
answer.append("L");
leftPoint = num;
}else {
answer.append("R");
rightPoint = num;
}
}
}
return answer.toString();
}
}
(*참고 : answer 를 String 으로 쓴 경우)
다른 사람의 답안
/* 코드 분리가 굉장히 잘 되어 있다고 생각되고
* 내 코드에선 어떻게든 효율적으로 알고리즘으로써 해결해보겠다고 했던 부분을
* 간단하게 그냥 수기작성(...)으로 처리했는데 이게 오히려 깔끔하다고 느꼈다.
* 사실 이런 적이 처음은 아닌데... 어쩔 땐 심플 이즈 베스트라는 것을 또 느끼는 순간.
*/
class Solution {
// 0부터 9까지 좌표 {y,x}
int[][] numpadPos = {
{3,1}, //0
{0,0}, //1
{0,1}, //2
{0,2}, //3
{1,0}, //4
{1,1}, //5
{1,2}, //6
{2,0}, //7
{2,1}, //8
{2,2} //9
};
//초기 위치
int[] leftPos = {3,0};
int[] rightPos = {3,2};
String hand;
public String solution(int[] numbers, String hand) {
this.hand = (hand.equals("right")) ? "R" : "L";
String answer = "";
for (int num : numbers) {
String Umji = pushNumber(num);
answer += Umji;
if(Umji.equals("L")) {leftPos = numpadPos[num]; continue;}
if(Umji.equals("R")) {rightPos = numpadPos[num]; continue;}
}
return answer;
}
//num버튼을 누를 때 어디 손을 사용하는가
private String pushNumber(int num) {
if(num==1 || num==4 || num==7) return "L";
if(num==3 || num==6 || num==9) return "R";
// 2,5,8,0 일때 어디 손가락이 가까운가
if(getDist(leftPos, num) > getDist(rightPos, num)) return "R";
if(getDist(leftPos, num) < getDist(rightPos, num)) return "L";
//같으면 손잡이
return this.hand;
}
//해당 위치와 번호 위치의 거리
private int getDist(int[] pos, int num) {
return Math.abs(pos[0]-numpadPos[num][0]) + Math.abs(pos[1]-numpadPos[num][1]);
}
}
// switch 의 case 를 이런 식으로도 쓸 수 있구나.
class Solution {
int tempL = 10;
int tempR = 12;
String myhand;
public String solution(int[] numbers, String hand) {
myhand = ((hand.equals("right"))? "R": "L");
String answer = "";
for(int i=0 ; i< numbers.length ; i++) {
switch(numbers[i]) {
case 1: case 4: case 7:
answer += "L";
tempL = numbers[i];
break;
case 3: case 6: case 9:
answer += "R";
tempR = numbers[i];
break;
default:
String tempHand = checkHand(numbers[i]);
if(tempHand.equals("R"))
tempR = numbers[i] + ((numbers[i] == 0)? 11:0);
else tempL = numbers[i] + ((numbers[i] == 0)? 11:0);
answer += tempHand;
break;
}
}
return answer;
}
private String checkHand(int tempNum) {
int leftDistance = 0;
int rightDistance = 0;
if(tempNum == 0) tempNum = 11;
leftDistance = Math.abs((tempNum-1)/3 - (tempL-1)/3) + Math.abs((tempNum-1)%3 - (tempL-1)%3);
rightDistance = Math.abs((tempNum-1)/3 - (tempR-1)/3) + Math.abs((tempNum-1)%3 - (tempR-1)%3);
System.out.println(tempNum + ": " + leftDistance + ", " + rightDistance);
return ((leftDistance == rightDistance)? myhand: (leftDistance > rightDistance)? "R": "L");
}
}
🔗링크
프로그래머스
코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.
programmers.co.kr