[오늘의 문제] 키패드 누르기

shlim55·2025년 7월 25일

코딩테스트

목록 보기
111/223

출처: https://school.programmers.co.kr/learn/courses/30/lessons/67256

문제 설명
스마트폰 전화 키패드의 각 칸에 다음과 같이 숫자들이 적혀 있습니다.

kakao_phone1.png

이 전화 키패드에서 왼손과 오른손의 엄지손가락만을 이용해서 숫자만을 입력하려고 합니다.
맨 처음 왼손 엄지손가락은 * 키패드에 오른손 엄지손가락은 # 키패드 위치에서 시작하며, 엄지손가락을 사용하는 규칙은 다음과 같습니다.

엄지손가락은 상하좌우 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 해주세요.
입출력 예
numbers hand result
[1, 3, 4, 5, 8, 2, 1, 4, 5, 9, 5] "right" "LRLLLRLLRRL"
[7, 0, 8, 2, 8, 3, 1, 5, 7, 6, 2] "left" "LRLLRRLLLRR"
[1, 2, 3, 4, 5, 6, 7, 8, 9, 0] "right" "LLRLLRLLRL"
입출력 예에 대한 설명
입출력 예 #1

순서대로 눌러야 할 번호가 [1, 3, 4, 5, 8, 2, 1, 4, 5, 9, 5]이고, 오른손잡이입니다.

왼손 위치 오른손 위치 눌러야 할 숫자 사용한 손 설명

  • 1 L 1은 왼손으로 누릅니다.

    1 # 3 R 3은 오른손으로 누릅니다.
    1 3 4 L 4는 왼손으로 누릅니다.
    4 3 5 L 왼손 거리는 1, 오른손 거리는 2이므로 왼손으로 5를 누릅니다.
    5 3 8 L 왼손 거리는 1, 오른손 거리는 3이므로 왼손으로 8을 누릅니다.
    8 3 2 R 왼손 거리는 2, 오른손 거리는 1이므로 오른손으로 2를 누릅니다.
    8 2 1 L 1은 왼손으로 누릅니다.
    1 2 4 L 4는 왼손으로 누릅니다.
    4 2 5 R 왼손 거리와 오른손 거리가 1로 같으므로, 오른손으로 5를 누릅니다.
    4 5 9 R 9는 오른손으로 누릅니다.
    4 9 5 L 왼손 거리는 1, 오른손 거리는 2이므로 왼손으로 5를 누릅니다.
    5 9 - -
    따라서 "LRLLLRLLRRL"를 return 합니다.

입출력 예 #2

왼손잡이가 [7, 0, 8, 2, 8, 3, 1, 5, 7, 6, 2]를 순서대로 누르면 사용한 손은 "LRLLRRLLLRR"이 됩니다.

입출력 예 #3

오른손잡이가 [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]를 순서대로 누르면 사용한 손은 "LLRLLRLLRL"이 됩니다.

내가 작성한 코드문

class Solution {
    public String solution(int[] numbers, String hand) {
        String answer = "";
        
        // 왼손 엄지손가락은 * 키패드에 오른손 엄지손가락은 # 키패드 위치에서 시작
        
        // 1, 4, 7을 입력할 때는 왼손 엄지
        // 3, 6, 9를 입력할 때는 오른손 엄지
        // 2, 5, 8, 0을 입력할 때는 두 엄지
        
        // 각 번호를 누른 엄지손가락이 왼손인 지 오른손인 지를 나타내는 연속된 문자열 형태로 return 
        // 좌표상 거리가 같은 경우 (2,5,8,0 누를 때) 왼손잡이 일시 왼손 오른손 잡이일시 오른손
        // 좌표 정보	
        // 손 위치 추적	
        // 거리 계산
        String destination = "";// 손 위치 추적	
        
        for(int i = 0; i < numbers.length; i++){
            if(numbers[i] == 1 || numbers[i] == 4 || numbers[i] == 7){
                // 거리계산 로직을 넣기, 좌표 정보라던가
                answer += "L";
            } else if(numbers[i] == 3 || numbers[i] == 6 || numbers[i] == 9){
                // 거리계산 로직을 넣기 
                answer += "R";
            } else {// 2,5,8,0일때
                // 거리계산 로직을 넣기 
                if(hand.equals("left")){// 왼손잡이
                    answer += "L";
                } else {// 오른손잡이
                    answer += "R";
                }
            }      
        }
        return answer;
    }
}

✅ 고쳐야 할 핵심:
중앙 숫자(2,5,8,0)를 누를 때:

왼손 위치와 해당 숫자까지의 거리 계산

오른손 위치와 해당 숫자까지의 거리 계산

누가 더 가까운지 비교

거리가 같으면 hand값을 따라 결정

그리고 손이 움직였을 경우, 그 손의 위치도 업데이트해야 한다.
예: 왼손이 2를 눌렀다면, 왼손의 현재 위치를 2로 바꿔줘야 다음 거리 계산이 정확해진다.

현재 코드에서 빠진 것설명
좌표 정보각 키패드 숫자의 좌표를 정의하지 않음
손 위치 추적왼손/오른손의 현재 위치를 업데이트하지 않음
거리 계산2, 5, 8, 0을 눌 때 거리 비교를 하지 않음

요구사항 반영된 코드문

class Solution {
    public String solution(int[] numbers, String hand) {
        StringBuilder answer = new StringBuilder();

        // 키패드 위치를 좌표로 표현
        int[][] keypad = {
            {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}; // # 위치

        for (int num : numbers) {
            if (num == 1 || num == 4 || num == 7) {
                answer.append("L");
                leftPos = keypad[num];
            } else if (num == 3 || num == 6 || num == 9) {
                answer.append("R");
                rightPos = keypad[num];
            } else {
                int[] target = keypad[num == 0 ? 0 : num];

                // 거리 계산
                int leftDist = Math.abs(leftPos[0] - target[0]) + Math.abs(leftPos[1] - target[1]);
                int rightDist = Math.abs(rightPos[0] - target[0]) + Math.abs(rightPos[1] - target[1]);

                if (leftDist < rightDist) {// 더 가까운 손이 누름
                    answer.append("L");
                    leftPos = target;
                } else if (leftDist > rightDist) {
                    answer.append("R");
                    rightPos = target;
                } else {// 거리가 같을 경우 hand 사용
                    if (hand.equals("left")) {
                        answer.append("L");
                        leftPos = target;
                    } else {
                        answer.append("R");
                        rightPos = target;
                    }
                }
            }
        }

        return answer.toString();// 문자열 반환
    }
}

StringBuilder answer:

L 또는 R 문자를 하나씩 추가하여 최종 결과 문자열을 효율적으로 만들어주는 도구다. String에 계속 += 하는 것보다 성능이 좋다.

int[][] keypad:

각 숫자에 해당하는 키패드 상의 좌표(y, x)를 저장한 2차원 배열입니다.

예를 들어, keypad[0]은 숫자 0의 위치인 {3, 1}을, keypad[1]은 숫자 1의 위치인 {0, 0}을 나타냅니다.

이 좌표들을 이용해 엄지손가락과 목표 숫자 사이의 거리를 계산한다.

int[] leftPos:

왼손 엄지손가락의 현재 위치를 저장하는 배열입니다. 초기 위치는 {3, 0}으로, 키패드의 * (별표) 버튼 위치이다.

int[] rightPos:

오른손 엄지손가락의 현재 위치를 저장하는 배열입니다. 초기 위치는 {3, 2}로, 키패드의 # (샵) 버튼 위치이다.

다른 사람의 코드문

class Solution {
    int tempL = 10;// 왼손 초기 위치 → *
    int tempR = 12;// 오른손 초기 위치 → #
    String myhand;// 주 손잡이 (R 또는 L)
    public String solution(int[] numbers, String hand) {
        myhand = ((hand.equals("right"))? "R": "L");// hand를 "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;// → 0일 경우 내부적으로 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);// → 숫자를 1부터 12까지로 보고
행: (num-1)/3 열: (num-1)%3 → 결과적으로 좌표처럼 맨해튼 거리 계산이 가능해짐

        System.out.println(tempNum + ": " + leftDistance + ", " + rightDistance);
        return ((leftDistance == rightDistance)? myhand: (leftDistance > rightDistance)? "R": "L");// 손 선택

    }
}

스위치문을 연달아 쓰는 방식

class Solution {
    public String solution(int[] numbers, String hand) {
        String answer = "";
        StringBuilder answerBuilder = new StringBuilder();
        String[][] phone = {// 키패드 구조를 문자열 2차원 배열로 표현:
                {"1", "2", "3"},
                {"4", "5", "6"},
                {"7", "8", "9"},
                {"*", "0", "#"},
        };
        int leftX = 3;// 왼손의 x (열) 좌표 → 처음엔 "*" 위치인 (3, 0)
        int leftY = 0;// 왼손의 y (행) 좌표
        int rightX = 3;// 오른손의 x 좌표 → "#" 위치인 (3, 2)
        int rightY = 2;
        for (int i = 0; i < numbers.length; i++) {
            for (int j = 0; j < phone.length; j++) {
                    for (int k = 0; k < phone[j].length; k++) {
                    if (String.valueOf(numbers[i]).equalsIgnoreCase(phone[j][k])) {// → 현재 숫자가 키패드 배열 phone[j][k] 위치에 있는지 확인 찾으면 그 좌표 (j, k)를 기반으로 판단
                        System.out.println("number[====="+numbers[i]);
                        if (k == 0) {// 왼쪽 열 숫자: 무조건 왼손 → 왼쪽 열 (1, 4, 7) → 왼손으로 누르고 좌표 갱신
                            answerBuilder.append("L");
                            leftX = j;
                            leftY = k;
                        } else if (k == 2) {// 오른쪽 열 숫자: 무조건 오른손 → 오른쪽 열 (3, 6, 9) → 오른손으로 누르고 좌표 갱신
                            answerBuilder.append("R");
                            rightX = j;
                            rightY = k;
                        } else {
                            int leftDistinct = Math.abs(j-leftX)+Math.abs(k-leftY);
                            int rightDistinct = Math.abs(j-rightX)+Math.abs(k-rightY);// → 맨해튼 거리 계산 방식으로 왼손/오른손 거리 비교
                            if (leftDistinct > rightDistinct) {
                                answerBuilder.append("R");
                                rightX = j;
                                rightY = k;
                            } else if (leftDistinct < rightDistinct) {
                                answerBuilder.append("L");
                                leftX = j;
                                leftY = k;
                            } else {
                                answerBuilder.append(hand.equalsIgnoreCase("right") ? "R" : "L");// 거리가 같으면 주 손잡이 사용 
                                if (hand.equalsIgnoreCase("right")) {
                                    rightX = j;
                                    rightY = k;
                                } else {
                                    leftX = j;
                                    leftY = k;
                                }
                            }
                        }
                    }
                }
            }
        }
        answer = String.valueOf(answerBuilder);
        return answer;
    }
}
profile
A Normal Programmer

0개의 댓글