본문 바로가기
JavaScript/Coding test

프로그래머스 코딩테스트 Lv.1 풀이 2

by enai 2024. 1. 11.

2023.12.22

이상한 문자 만들기

function solution(s) {
    const answer = s.split(' ').map((word) => {
        return word.split('').map((char, index) => {
            if (index % 2 === 0) {
                return char.toUpperCase()
            }
            return char.toLowerCase()
        }).join('')
    }).join(' ')
    return answer;
}

문자열 s를 단어 단위로 자른 후, 각 단어들의 글자들의 index가 짝수면 대문자로(toUpperCase), 아니면 소문자로(toLowerCase) 바꾸고 다시 합쳐서 반환
삼항연산자로 더 간결하게 표현할 수 있었을 거 같아서 아쉬움

크기가 작은 부분문자열

function solution(t, p) {
    const pN = parseInt(p)
    const pLength = p.length
    const limit = t.length - pLength + 1
    let answer = 0
    for (let i = 0; i < limit; i++) {
        const n = parseInt(t.substring(i, pLength + i))
        if (n <= pN) {
            answer++
        }
    }
    return answer;
}

t를 p의 길이만큼 자르고, p와 비교해서 더 작거나 같으면 answer에 1을 더함
t의 길이가 p의 길이보다 더 짧을 땐 비교해볼 필요 없으므로 for문의 limit를 t 길이 
- p 길이 + 1로 설정함

삼총사

function solution(number) {
    let answer = 0;
    
    const combination = (current, start) => {
        if (current.length === 3) {
            answer += current.reduce((acc, curr) => acc + curr, 0) === 0 ? 1 : 0
            return
        }
        for (let i = start; i < number.length; i++) {
            combination([...current, number[i]], i + 1)
        }
    }

    combination([], 0)

    return answer;
}

풀리지 않아서 문제 풀이를 찾아봄
재귀함수로 각 요소들을 모두 확인할 수 있음
반복문으로 number의 요소들을 current에 하나씩 넣어주고 current의 길이가 3이면 합을 구해 0이 되는지 확인함
다른 풀이를 보니 for문을 3번 쓰는 걸로도 풀 수 있는 걸 알게됨
. 이거라도 시도해볼 걸 아쉬움이 남음

2023.12.23

최소직사각형

function solution(sizes) {
    const widths = []
    const heights = []
    for (const [w, h] of sizes) {
        if (w < h) {
            widths.push(h)
            heights.push(w)
            continue;
        }
        widths.push(w)
        heights.push(h)
    }
    const answer = Math.max(...widths) * Math.max(...heights)
    return answer;
}

sizes의 가로길이, 세로길이를 비교해 더 큰 쪽을 가로길이, 작은 쪽을 세로길이로 바꿈
각 가로들
, 세로들 중 가장 큰 값을 구해 반환

시저 암호

function solution(s, n) {
    const answer = s.split('').reduce((acc, curr) => {
        if (/[a-zA-Z]/.test(curr)) {
            const originalCode = curr.charCodeAt(0)
            const code = curr.charCodeAt(0) + n
            if (originalCode <= 90 && code > 90 || code > 122) {
                return acc + String.fromCharCode(code - 26)
            }
            return acc + String.fromCharCode(code)
        }
        return acc + curr
    }, '')
    
    return answer;
}

각 글자의 유니코드를 구해 + 1을 하여 다음 글자의 유니코드를 구함
z 다음은 a
, Z 다음은 A가 되도록 다음 유니코드가 알파벳이 아니면 - 26을 해줌 (알파벳 개수: 26개)

숫자 문자열과 영단어

function solution(s) {
    const numbers = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']
    const answer = s.split('').reduce((acc, curr) => {
        const currText = acc + curr
        const prevNumber = currText.match(/\d+/) ?? ''
        const text = currText.match(/[a-z]+/)?.[0]
        const number = numbers.indexOf(text)
        if (number > -1) {
            return prevNumber + number
        }
        return currText
    })
    return parseInt(answer);
}

numbers에 숫자 영단어를 차례대로 저장
문자열을 각 문자별로 배열로 만듦
글자 배열을
reduce로 순회하며 이때까지의 문자열이 numbers에 있는지 확인
있으면 해당
index로 바꿔주기, 없으면 그대로 반환
그렇게 누적된 index 문자열을 숫자로 바꿔 반환

2023.12.24

가장 가까운 같은 글자

function solution(s) {
    const answer = []
    const storage = []
    s.split('').map((char) => {
        const index = storage.indexOf(char)
        answer.push(index + (index > -1))
        storage.unshift(char)
    })
    return answer;
}

storage 에 해당 글자가 있는 index를 가져와 answer에 추가, storage에 앞쪽에 글자 추가
storage에 없는 경우 -1, 있으면 해당 위치의 index + 1answer에 추가됨

문자열 내 마음대로 정렬하기

function solution(strings, n) {
    const collator = new Intl.Collator('en');
    const answer = strings.sort((prev, curr) => {
        const compareCount = collator.compare(prev[n], curr[n])
        if (compareCount === 0) {
            return collator.compare(prev, curr)
        }
        return compareCount
    })
    return answer;
}

Intl.Collator.compare로 문자열 비교 가능 (localeCompare로도 가능) (같으면 0, 크면 1, 작으면 -1 반환)
글자끼리 비교 후
, 같으면 전체 단어끼리 비교함

K번째수

function solution(array, commands) {
    const answer = commands.map(([i, j, k]) => array.slice(i - 1, j).sort((a, b) => a - b)[k - 1])
    return answer;
}

arraycommands에 주어진 대로 자른 후, 오른차순으로 정렬하고 k번째 수를 반환함

2023.12.25

[1차] 비밀지도

function solution(n, arr1, arr2) {
    const answer = arr1.map((row, i) => {
        const password1 = row.toString(2)
        const password2 = arr2[i].toString(2)
        const password1Arr = [...new Array(n - password1.length).fill(0), ...password1]
        const password2Arr = [...new Array(n - password2.length).fill(0), ...password2]
        return password1Arr.map((el, j) => {
            if (!!parseInt(el) || !!parseInt(password2Arr[j])) {
                return '#'
            }
            return ' '
        }).join('')
    })
    return answer
}

각 행들을 2진수로 바꾸고 배열로 만든 후, 지도 길이에 모자라는 만큼 앞에 0을 채워줌
해당 배열의 요소가 하나라도 0이 아니면
'#', 모두 0이면 ' '을 반환하고, 다시 문자열로 만듦
이 문자열들을 담음 배열 반환
+) repeat이나 padStart로 0을 채우고, 정규표현식replace로 더 간단하게 풀 수 있음

푸드 파이트 대회

function solution(food) {
    let answer = ''
    for (let i = 1; i < food.length; i++) {
        const count = Math.floor(food[i] / 2)
        if (count > 0) {
            answer += `${i}`.repeat(count)
        }
    }
    answer += + '0' + answer.split('').reverse().join('')
    return answer;
}

상대방과 똑같은 수를 먹어야 하기 때문에 food 개수를 2로 나눈 몫이 0보다 큰지 확인
answer에 해당 음식 번호를 먹을 수 있는 개수만큼 나열 (수웅이 먹을 음식 번호가 됨)
0을 붙이고 수웅이 먹을 음식 번호를 뒤집어서 붙임

두 개 뽑아서 더하기

function solution(numbers) {
    const answer = [];
    for (let i = 0; i < numbers.length - 1; i++) {
        for (j = i + 1; j < numbers.length; j++) {
            const summary = numbers[i] + numbers[j]
            if (!answer.includes(summary)) {
                answer.push(summary)
            }
        }
    }
    return answer.sort((a, b) => a - b);
}

두개의 for문으로 numbers의 두 요소를 더해서 answer에 담음
answer에 이미 있는 값이면 패스함
+) Set으로도 중복을 없앨 수 있음

2023.12.26

콜라 문제

function solution(a, b, n, answer = 0) {
    if (n < a) return answer

    const received = Math.floor(n / a) * b
    const given = received / b * a
    return solution(a, b, n - given + received, answer + received)
}

받은 콜라병은 가지고 있는 빈병n을 마트에 줘야하는 병 수 a로 나눈 후, 마트에서 a 병마다 주는 병 수 b를 곱해서 구함
마트에 넘기는 콜라병 수는 받은 콜라병 수에서
b를 나누고 a를 곱하여 구함
solution 함수에 가지고 있는 빈 병 수를 업데이트 해서 넘김, 이때 받은 병 수의 합을 구해야 함으로 마지막에 받은 병 수를 더한 값도 같이 넘김
남은 병 수가 마트에 줄 수 있는 병 수보다 적으면 받았던 병수 총합을 반환

추억 점수

function solution(name, yearning, photo) {
    const answer = photo.map((item) => {
        return item.reduce((acc, curr) => {
            const yearningIndex = name.indexOf(curr)
            if (yearningIndex > -1) {
                return acc + yearning[yearningIndex]
            }
            return acc
        }, 0)
    })
    return answer;
}

photo의 이름이 name에 있는지 확인하고, 해당 name의 위치에 있는 추억 점수를 더함

명예의 전당 (1)

function solution(k, score) {
    const storage = []
    const answer = score.map((n) => {
        storage.push(n)
        storage.sort((a, b) => a - b)
        if (storage.length > k) {
            storage.shift()
        }
        return storage[0]
    })
    return answer;
}

score을 돌면서 storage에 저장
storage를 오름차순으로 정렬하고 k개보다 많으면 제일 앞 요소(제일 작은 점수)를 제거
매 점수 요소마다
storage의 제일 앞 요소를 answer 배열에 저장하여 반환

2023.12.27

카드 뭉치

function solution(cards1, cards2, goal) {
    let answer = true;
    for (let i = 0; i < goal.length; i++) {
        const word = goal[i]
        if (cards1[0] === word) {
            cards1.shift()
        } else if (cards2[0] === word) {
            cards2.shift()
        } else {
            answer = false
            break;
        }
    }
    return answer ? 'Yes' : 'No';
}

카드 뭉치 순서대로 단어를 뽑을 수 있으므로 제일 앞인 0번 index의 카드가 goal의 단어와 맞는지 확인 맞으면 해당 카드를 제거 -> 반복
두 카드 뭉치 제일 앞에 없으면 뽑을 수 없으므로
answerfalse로 변경
answertrue'Yes', false'No' 반환

2016년

function solution(a, b) {
    const dayOfWeek = ['FRI', 'SAT', 'SUN', 'MON', 'TUE', 'WED', 'THU']
    const days = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    const currDays = days.slice(0, a - 1).reduce((acc, curr) => acc + curr, 0) + b
    const i = currDays % 7
    return dayOfWeek[i === 0 ? 6 : i - 1]
}

dayOfWeek: 요일 배열, 1월 1일이 금요일이므로 'FRI'를 제일 첫번째 요소로 넣음
days: 각 월 별 일수 배열
currDays: a월 직전달까지의 일자수 + b일 => 1월 1일 ~ a월 b일까지의 일자 수
currDays % 7로 몇번째 요일인지 가져옴
dayOfWeek[i === 0 ? 6 : i - 1]: dayOfWeek에서 index로 가져와야하기 때문에 i - 1, 7로 나눠 떨어지는 경우, 마지막 요소이므로 i===0일 때는 6으로 바꿔줌

폰켓몬

function solution(nums) {
    const mons = []
    for (const num of nums) {
        if (!mons.includes(num)) {
            mons.push(num)
        }
        if (mons.length === nums.length / 2) break;
    }
    return mons.length;
}

최대한 고를 수 있는 폰켓몬 종류를 구하기 위해 중복되는 폰켓몬을 제거하면 되겠다고 생각함
nums를 반복문으로 돌면서 mons 배열에 없는 요소일 때만 mons에 담음
mons 개수가 N/2가 되면 멈춤 (최대 N/2개만 가져갈 수 있기 때문)
mons 개수가 최대한 가질 수 있는 폰켓몬 개수가 됨
+) Set으로 중복 제거할 수 있다는 걸 또 떠올리지 못 함... 다음엔 떠올릴 수 있기를...😂

2023.12.28

과일 장수

function solution(k, m, score) {
    const answer = score.sort((a, b) => b - a).reduce((acc, curr, i) => {
        if ((i + 1) % m === 0) {
            return acc + curr * m
        }
        return acc
    }, 0)
    return answer;
}

큰 수부터 m개씩 묶으면 그 중 가장 작은 수가 최저 사과 점수가 됨
=> 사과 점수를 내림차순으로 정렬해서 m으로 나눠 떨어지는 번호의 사과 === 각 상자의 최저 사과 점수 최저 사과 점수 * m 한 걸 모두 더해서 구함

모의고사

function solution(answers) {
    const one = [1,2,3,4,5]
    const oneLength = one.length
    const two = [2,1,2,3,2,4,2,5]
    const twoLength = two.length
    const three = [3,3,1,1,2,2,4,4,5,5]
    const threeLength = three.length
    let score = [0, 0, 0]
    for (let i = 0; i < answers.length; i++) {
        const answer = answers[i]
        if (answer === one[i % oneLength]) {
            score[0] += 1
        }
        if (answer === two[i % twoLength]) {
            score[1] += 1
        }
        if (answer === three[i % threeLength]) {
            score[2] += 1
        }
    }
    const maxScore = Math.max(...score)
    return score.map((s, i) => s === maxScore ? i + 1 : undefined).filter((n) => !!n);
}

정답과 수포자가 찍은 답을 비교해 score 배열에 순서대로 저장
score에서 제일 큰 점수를 찾아 해당하는 번호 반환

소수 만들기

// 제곱근 이하로 나눠지지 않으면 소수인 공식 이용
function checkIsPrimeNumber(n) {
    if (n === 1) return false
    if (n <= 3) return true
    for (let i = 2; i <= Math.sqrt(n); i++) {
        if (n % i === 0) return false
    }
    return true
}
// 삼총사 때 했던 재귀함수 이용
function solution(nums) {
    let answer = 0;
    
    const groupNums = (grouping, start) => {
        if (grouping.length === 3) {
            answer += checkIsPrimeNumber(grouping.reduce((acc, curr) => acc + curr))
            return
        }
        
        for (let i = start; i < nums.length; i++) {
            groupNums([...grouping, nums[i]], i + 1)
        }
    }
    groupNums([], 0)
    
    return answer;
}

삼총사 때 봤던 대로 재귀함수로 nums를 3개씩 탐색
제곱근 이하의 수로 나눠지지 않으면 소수라는 공식을 이용해 소수인지 아닌지 확인
소수가 맞을 때만
answer+1 해줌

2023.12.29

소수 찾기

function solution(n) {
    let answer = n - 1
    for (let i = 3; i <= n; i++) {
        if (i % 2 === 0) {
            answer--
            continue
        }
        
        for (let j = 3; j <= Math.sqrt(i); j += 2) {
            if (i % j === 0) {
                answer--
                break
            };
        }
    }
    return answer
}

answer에 2~n까지의 개수 저장 (1은 소수가 아니기 때문)
for문을 이용해 숫자 하나씩 확인해 소수가 아니면 answer에서 하나씩 빼기 (2는 소수이기 때문에 3부터 확인)
짝수면 소수가 아니기 때문에
answer에서 빼주고
제곱근까지 나눠서 나누어 떨어지는 숫자 역시 소수가 아니기 때문에
answer에서 빼줌

기사단원의 무기

function solution(number, limit, power) {
    const knightArr = []
    for (let i = 1; i <= number; i++) {
        let count = 0
        const middle = Math.sqrt(i)
        for (let j = 1; j <= middle; j++) {
            if (j === middle) {
                count++
                continue
            }
            if (i % j === 0) count += 2
        }
        knightArr.push(count) // 여기서 바로 limit 체크해서 계산해도 됐을 듯
    }
    const answer = knightArr.reduce((acc, curr) => {
        if (curr > limit) {
            return acc + power
        }
        return acc + curr
    }, 0)
    return answer;
}

약수의 개수는 제곱근까지의 개수의 두배라는 공식 이용
약수의 개수를 구해 기사별로 가질 수 있는 공격력을 knightArr에 담음 후
, limit랑 비교해 넘는 경우엔 power로 바꿔서 누적 합 구해 반환

덧칠하기

function solution(n, m, section) {
    let answer = 0
    let paintStartPoint = 0
    Array(n).fill().map((_v,i) => i+1).map((curr) => {
        if (section.includes(curr) && !paintStartPoint) {
            paintStartPoint = curr
            answer++
        }
        if (paintStartPoint + m - 1 === curr) {
            paintStartPoint = 0
        }
    })
    
    return answer;
}

처음 칠해야 하는 영역을 찾고, 페인트가 시작되는 번호를 paintStartPoint에 저장
paintStartPoint
+ m - 1 (한번에 칠할 수 있는 마지막 번호)가 되면 paintStartPoint를 0으로 리셋
다음 칠해야 하는 영역이 됐을 때 paintStartPoint가 없으면 다시 반복
칠해질 때마다 answer에 카운트 추가
answer는 총 페인트를 칠하게 된 횟수가 됨

2023.12.30

실패율

function solution(N, stages) {
    let failureRates = []
    let passedCount = stages.length
    const sortedStages = stages.sort((a, b) => a - b).reduce((acc, curr) => {
        return {...acc, [curr]: acc.hasOwnProperty(curr) ? ++acc[curr] : 1}
    }, {})

    for (let level = 1; level <= N; level++) {
        const failedCount = sortedStages[level] ?? 0
        const failureRate = failedCount / passedCount
        passedCount -= failedCount
        failureRates.push([level, failureRate])
    }

    const answer = failureRates.sort((a, b) => b[1] - a[1]).map(([level, failureRate]) => level)
    return answer;
}

멈춰있는 스테이지 인원 수 저장 (sortedStages)
실패율
=== 스테이지 통과 실패한 사람 수 / 스테이지 통과한 사람 수 (해당 레벨보다 큰 사람 수)
[
레벨, 실패율]로 만들어서 실패율을 내림차순으로 정렬해서 레벨만 뽑아 배열로 만들어 반환

[1차] 다트 게임

function solution(dartResult) {
    const score = dartResult.split('').reduce((acc, curr, index, src) => {
        if (/\d/.test(curr)) {
            const prev = src[index - 1]
            if (/\d/.test(prev)) {
                acc.pop()
                return [...acc, prev + curr]
            }
            return [...acc, curr]
        }
        if (/[S|D|T]/.test(curr)) {
            const newScore = Math.pow(acc.pop(), curr === 'S' ? 1 : curr === 'D' ? 2 : 3)
            return [...acc, newScore]
        }
        if (curr === '#') {
            const newScore = acc.pop() * -1
            return [...acc, newScore]
        } else if (curr === '*') {
            const currScore = acc.pop()
            const prevScore = acc.pop()
            return [...acc, (prevScore ?? 0) * 2, currScore * 2]
        }
    }, [])
    return score.reduce((acc, curr) => acc + curr)
}

dartResult를 배열로 만들어서 한 요소씩 체크해서 score 배열에 계산해서 저장할 예정
숫자면 score에 추가
, 이전 요소가 숫자면 score에 추가했던 걸 빼고 이전 숫자와 더해서 다시 score에 추가
S면 score에 1제곱 D면 2제곱 T면 3제곱해서 저장
#이면 -1 곱하고, *이면 마지막 요소와 그 이전 요소까지 가져와서 x2해서 다시 저장

옹알이 (2)

function solution(babbling) {
    let answer = 0;
    babbling.forEach((word) => {
        const rest = word.replaceAll(/(aya(?!aya)|ye(?!ye)|woo(?!woo)|ma(?!ma))/g, '')
        if (!rest) answer++
    })
    return answer;
}

바로 뒤에 오는지 확인하는 정규표현식 ?! 이용해서 바로 뒤에 같은 단어는 오지 않는 단어들을 모두 찾아서 빈값으로 바꿈
빈값이면 모든 단어를 말할 수 있는 거기 때문에 카운트함 총합 반환

 

댓글