#0x03. 잡다한 작업들 #2

Leo Cho·2022년 10월 16일
0
post-thumbnail

PS 🎯

이번 주에도 프로그래머스를 집중적으로 팠습니다. 오랜만에 다시 시작하는 PS이니만큼 쉬운 문제들을 좀 풀면서 적응하고, JS로 풀이하는 훈련도 하려 했는데...
무려 레벨 0 100제가 새로 나왔네요.
코테나 PS에 처음 입문하거나 레벨 1도 버거운 초심자들을 위해 제작했다고 합니다.
문제 대부분의 난이도가 굉장히 쉽기 때문에 금주 풀었던 문제들 중 특히 어려웠거나 생각할 거리가 있었던 문제들을 중심으로 작성하려 합니다.

옹알이 (레벨 0) 🍼

주어진 문자열을 "aya", "ye", "woo", "ma" 네 개의 문자열이 반복되지 않는 조합으로 만들 수 있는지 묻는, 문자열 구현 문제입니다. 일단 문자열 중 완벽하게 겹치는 형태가 없으니 쉽게 느껴질 수 있지만, 이런 유형의 문제가 처음이라 조금 시간이 걸렸습니다. ⏱️

풀이는 이렇게 했습니다:

function solution(babbling) {
    let result = 0;
    for (let word of babbling) {
        if (word.replace(/(aya)|(ye)|(woo)|(ma)/g, '') == '' &&
            !word.match(/(ayaaya)|(yeye)|(woowoo)|(mama)/g)) {
            result++;
        }
    }
    return result;
}

정규표현식의 반복 패턴 변경자를 이용한 풀이입니다. regexp 짱!

제곱수 판별하기 (레벨 0) ✖️✖️

난이도 측면에서든, 개념 측면에서든 여기서 다루는 문제 중에서 가장 쉬웠지만, 한 번은 짚고 넘어갈 필요가 있다고 생각한 문제였습니다.
단순히 주어진 자연수가 어떤 자연수의 제곱수면 1을, 아니면 2를 return하는 문제였습니다. 처음에는 단순히 지수 연산자를 이용해서 풀었는데,

function solution(n) {
    return ((n ** 0.5) ** 2 == n) ? 1 : 2
}

여기서 치명적인 실수는, (n ** 0.5) ** 2는 결국 n과 같기에 제곱수를 판별하는데 의미가 없는 식이었습니다. 사소한 구현상의 실수였지만, 실수 단위에서 문제를 다룰 때는 더욱 신중할 필요가 있다는 교훈을 주었습니다.

올바른 풀이:

function solution(n) {
    return (Math.floor(n ** 0.5) ** 2 == n) ? 1 : 2
}

영어가 싫어요 (레벨 0) 🔡😩

앞의 '옹알이' 문제와 맥락은 같지만, 저의 풀이 방식은 상당히 상이했던 문제였습니다. 영어로 나타낸 숫자가 붙어 있는 배열을 아라비아 숫자로 변화하는 문제로, 마찬가지로 영어 숫자 사이에는 겹치는 부분이 없기 때문에 각 자리 사이 연관성은 없습니다.

ston = { // means string to number
    'one': 1,
    'two': 2,
  	 ...
}

function solution(numbers) {
    let result = '';
    Loop: while (true) {
        for (let i in ston) {
            if (numbers.startsWith(i)) {
                result += ('' + ston[i]);
                numbers = numbers.slice(i.length);
            }
            if (!numbers) {
                break Loop;
            }
        }
    }
    return +result;
}

사실, 다시 보니 while (numbers)로 바꾸면 굳이 break문을 넣어줄 필요는 없었네요 😅 일단 맞고 보자는 생각이었던 거 같습니다;

평행 (레벨 0) 📏

웬지 이 음악과 함께 풀어야 할 것 같은 문제네요. ➡️ 🧷📻
네 점의 좌표가 주어질 때, 이들을 각각 이은 두 쌍의 직선이 평행한 경우가 있는지 판별하는 문제입니다.

function isParallel(A1, A2, B1, B2) {
    return ((A1[0] == A2[0] && B1[0] == B2[0]) ||
    ((A2[1] - A1[1]) / (A2[0] - A1[0]) == (B2[1] - B1[1]) / (B2[0] - B1[0])))
}

function solution(dots) {
    return +(
        isParallel(dots[0], dots[1], dots[2], dots[3]) ||
        isParallel(dots[0], dots[2], dots[1], dots[3]) ||
        isParallel(dots[0], dots[3], dots[1], dots[2])
    )
}

이 풀이에서 함수 isParallel은 두 직선 A1A2\overleftrightarrow{A_1A_2}, B1B2\overleftrightarrow{B_1B_2} 이 평행한지 판별하는 함수인데, 여기서 주의하며 구현했던 점은 선분이 x축과 수직일 때입니다. x의 증분이 0이 되면 Division by zero가 되니, x좌표가 서로 같은지에 대한 조건을 따로 달았습니다.

Javascript는 0으로 나누면 Infinity를 뱉으니 상관없지 않나요? 🤔

음... 그건 JS가 이상한 거니까 😑

키패드 누르기 (레벨 1) 🫱📱🫲

과거 2학년 꼬꼬마 시절 때 풀지 못했던 문제입니다. 1년 뒤 다시 풀어 보니 느낌이 새롭네요.
제작년 카카오 인턴쉽에서 출제된 문제인데,
각 숫자 자판을 정해진 로직에 맞춰 입력하는 손가락을 맞추는 문제입니다.

positions = {
    1: [0, 0], 2: [0, 1], 3: [0, 2],
    4: [1, 0], 5: [1, 1], 6: [1, 2],
    7: [2, 0], 8: [2, 1], 9: [2, 2],
               0: [3, 1]
};

function compareDistance(l, r, t) {
    let leftDistance = Math.abs(l[0] - t[0]) + Math.abs(l[1] - t[1]);
    let rightDistance = Math.abs(r[0] - t[0]) + Math.abs(r[1] - t[1]);
    return (leftDistance < rightDistance ? 'left' : (
        leftDistance > rightDistance ? 'right': 'same'
    ));
}

function solution(numbers, hand) {
    let answer = '';
    let left = [3, 0]; let right = [3, 2]; // 각 손의 위치
    for (let digit of numbers) {
        if ([1, 4, 7].includes(digit)) {
            answer += 'L';
            left = positions[digit];
        } else if ([3, 6, 9].includes(digit)) {
            answer += 'R';
            right = positions[digit];
        } else {
            let compareDistanceResult = compareDistance(left, right, positions[digit]);
            if (compareDistanceResult == 'left') {
                answer += 'L';
                left = positions[digit];
            } else if (compareDistanceResult == 'right') {
                answer += 'R';
                right = positions[digit];
            } else {
                if (hand == 'left') {
                    answer += 'L'; left = positions[digit]
                } else {
                    answer += 'R'; right = positions[digit]
                }
            }
        }
    }
    return answer;
}

정말 크고 아름다운 코드네요. 어디서부터 설명을 적어야 할지 모르겠습니다.
풀이를 찾아보니 /[147]/.test(digit) 이렇게 regexp를 이용해서 자릿수를 거르는 방법도 있더군요. 배울 점이 많았던 문제였던 것 같습니다.

영어 끝말잇기 (레벨 2) ⛓️

영어 끝말잇기를 진행하다가 처음으로 틀린 사람의 번호와 순서를 계산하는 문제입니다. 개념에 비해 구현 난이도만 높아서 그런지 레벨 1에 있어도 될 것 같은 문제였죠.

function solution(n, words) {
    let lastLetter = '';
    let wordList = [];
    let turn = 0;
    let isProblem = false;
    Loop: for (let word of words) {
        turn++;
        if (!word.startsWith(lastLetter) ||
            wordList.includes(word)) {
            isProblem = true;
            break Loop;
        }
        lastLetter = word.at(-1);
        wordList.push(word);
    }
    if (isProblem) {
        return [turn%n == 0 ? n : turn%n, Math.ceil(turn/n)]
    } else {
        return [0, 0];
    }
}

멀리 뛰기 🏃

예전에 자주 풀었던 피보나치 수열 유형의 dp 문제였습니다. 이건 문제자체보다는 풀이 때문에 기억에 남는 문제인데,

function solution(n) {
    let fib = [1, 2];
    for (let i=2; i<=2000; i++) {
        fib.push((fib.at(-1) + fib.at(-2)) % 1234567);
    }
    return fib[n-1]
}

이렇게 dp로 구현할 문제를 제한 범위 n<2000n<2000 내의 fib 배열을 통째로 집어넣은 광인을 만났습니다... 여기서 볼 수 있습니다.

아희... 엄랭... 그 다음은? 🧠

여러 가지 한글 Esolang들을 접하면서, 저도 이런 언어를 하나쯤 만들고 싶어졌습니다. 처음 생각했던 건 아임뚜렛 밈에서 파생한 아잇...어였지만, 인기가 다 식어서 보류하기로 했습니다.

뭉'랭'이도 고려해보았지만, 이미 있는 언어더군요. 그래서 어쩔 수 없이 아잇!어를 만들어보기로 했습니다. 제 손으로 구현체까지 직접 만들어서요.

아희 (...)

PS 문제를 하나 풀려 했는데, 아희 자제로 알고리즘을 짜는 게 쉽지가 않더군요.
방방다망함
한 줄 던지고 말겠습니다. print(sum(map(int, input().split())))와 동치입니다.

profile
취미와 진로 사이, JS를 사랑하는 중학생 개발 지망생

0개의 댓글