프로그래머스 Lv.0 연습문제 정리 #2 32문제

세나정·2023년 4월 13일
0

시프트 연산자 <<
9 << 3 은 9 * (2^3) = 72


369 게임


각자리가 3의 배수일 때마다 박수를 치고 총 박수친 횟수를 return

나는 보통 숫자를 배열로 바꿀 때 ex) num = 123
String(num).split('') 를 많이 사용 했는데
스프레드 문법을 사용하면 편하다
[...order+' '] 대신 여기서 +' ' 부분을 꼭 넣어줘야함

아래처럼 배열로 바꾸어준 다음 filter를 활용하는데 includes를 활용하면 Number로 형변환을 안 해줘도 돼서 편함


숫자 찾기

문자열로 바꾼 후 includes를 활용하여 포함한다면
indexOf 를 활용하여 index값+1 (번째를 물어봤으니) 를 반환

indexOf는 문자열에도 사용이 가능함

★ 합성수 찾기

결국 풀지 못했던 문제..
답을 찾아보니 이중 포문을 통해
주어진 n전에 숫자들이 합성수인지 아닌지를 판단
cnt로 체크한 약수(cnt)가 3개 이상이라면 전역 변수인 answer의 값을 +1씩 올려주는 식으로 해서 해당 숫자 전에 나오는 모든 정수들의 약수가 3개 이상일 때마다 +1

여기서 유의해야할 것은 inner for문과 if문이 같은 들여쓰기 위치에 있어서 병렬적으로 동작한다는 것

function solution(n) {
    
    var answer = 0
    
    for(let i = 1; i <= n; i++) {
        let cnt = 0
        // 6이 들어왔다고 하면 
        // i j
        // 1 1 cnt 1 answer 0
        
        // 2 1 cnt 1 answer 0
        // 2 2 cnt 2 answer 0
        
        // 3 1 cnt 1 answer 0
        // 3 2 cnt 1 answer 0
        // 3 3 cnt 2 answer 0
        
        // 4 1 cnt 1 answer 0
        // 4 2 cnt 2 answer 0
        // 4 3 cnt 2 answer 0
        // 4 4 cnt 3 answer 1 
        
        for(j = 1; j <= i; j++) {
            if(i % j === 0) cnt ++
        }
        
        if(cnt > 2) {
            answer += 1
        }
    }
    
    return answer
}

문자열 정렬하기 (2)

대문자를 소문자로 찝어서 바꾸는 일이 없길..
그냥 모두다 소문자로 바꾸어주면됨

my_stinrg.toLowerCase().split('').sort().join('')

중복된 문자 제거 set

당연히 보자마자 set을 떠올렸다
제공된 문자열을 ex) my_string = "people"
const set = new Set(my_string) 을 통해 객체로 만들어준 후 // { 'p', 'e', 'o', 'l' }
다시 전개 연산자를 통해 배열로 바꾼 후
join을 통해 문자열로 합치면 됨 [...set].join('')


모스부호 (1)

객체의 키에 해당하는 값을 가져와서 문자열로 반환하는 문제

문제에선 letter = ".... . .-.. .-.. ---" 처럼 공백으로 구분하여 암호가 주어짐
가장 먼저 해야할 건 "공백으로"라는 말에 초점을 두고 바로 split(" ")을 하는 것

그렇게 만들어진 인덱스들과 morse와 비교하여 return 하면 됨

return letter.map( v => morse[v]).join('')
letter의 인덱스값들이 morse의 키에 접근해서 나온 값들을
join을 통해 문자열로 변환하여 반환

혹은 이렇게 보다 깔끔하게 reduce를 써도 됨


A로 B만들기

어렵게 생각할 거 없이 before와 after를 정렬했을 때 둘의 값이 같은지 다른지 보면됨


★ 팩토리얼

아쉬움이 많이 남는 문제인 것 같다.. 팩토리얼 구현까지는 알고 있었지만
i를 return 하는 과정에서 버벅거림

팩토리얼을 구현한 후
n이 factorial(i) 보다 작아질 때가 오면 그때에 해당하는 팩토리얼의 전 수를 반환
여기서 i가 11까지인 이유는 factorial 10까지를 고려해야하기 때문


★ 2차원으로 만들기 splice


splice를 활용하여 원본 배열이 변경된다는 걸 알고 이용하면 좋음

// num_list의 길이가 존재할 때 (1이상일 때)
let ans =[];
while (num_list.length) {
	// 처음부터 2개씩을 넣어주세요 (길이가 0될 때 까지)
	ans.push(num_list.splice(0, n)
}

return ans;

★ 가까운 수

function solution(array, n) {
    // n을 뺀 절대값과 array를 벗긴 값들 중에 n을 뺀 값 
    // (둘이 같은 말)과 일치하는 것을 지정
    
    // 즉, 절대값의 차이가 가장 작거나 같은 애들을 filter로 선정
    // 그 후, 	오름차순 정렬한 후 가장 작은 애를 제거
    
	// Math.min 사이엔 정수가 들어가야 하기 때문에 전개연산자를 사용하여 같은값을 비교해줌
    
	return array.filter( v => Math.abs(v-n) === 
    Math.min(...array.map(v => Math.abs(v-n))
    .sort((a, b)=> a-b).shift()
}

진료순서 정하기 swallow copy & deep copy

emergency = [3, 76, 24];

여기서
array = emergency.sort( (a, b) => b-a )
와 array = [...emergency.sort( (a, b) => b-a ) 의 차이

이것은 참조연산 때문에 메모리 값을 직접 수정하느냐 (deep copy)
아니면 그 메모리 값을 가리킬 뿐이냐 (swallow copy) 의 차이

그렇기 때문에 전자는 emergency의 값이 바뀌어 버리고
후자는 메모리 주소를 가리키는 화살표만 복사하는 것이기 때문에 바뀌지 않음

나는 전자를 사용해서 자꾸 엉뚱한 답이 나왔었음

function solution(emergency) {
    // 내림차순 정렬 [76, 24, 3]
	array = [...emergency].sort( (a,b) => b - a) 
    
    //  3이 array에서 어디에 있는가 인덱스 2 (즉, +1하여 3번째 위치)
    // map을 통한 반복으로 결과들을 배열에 모아줌
    return emergency.map( v => array.indexOf(v)+1 )
}

다른사람 답

sort 는 기존 배열을 변경하여 요소를 정렬 시킴.
slice()는 emergency 배열을 변경하지 않고 새로운 배열을 할당해주는 역할을 함

즉, slice()의 유무는 swallow copy인가 deep copy인가의 차이

function solution(emergency) {
    let sorted = emergency.slice().sort((a,b)=>b-a);
    return emergency.map(v=>sorted.indexOf(v)+1);
}

나처럼 전개연산자를 통해 sallow copy를 해도 됨 

★ 숨어있는 숫자의 덧셈 (2)

이 문제 덕분에 오랜만에 정규표현식을 복습했다.

처럼 붙어있는 숫자라면 붙어서 빼야하므로
정규표현식에 match를 활용하면 된다

match : ("문자열").match(/정규표현식/플래그) : "문자열"에서 "정규표현식"에 매칭되는 항목들을 배열로 반환

[ ] : 괄호안에 문자들 중 하나 or처리묶음이라고 보면 됨
/[abc]/ : "a" 또는 "b" 또는 "c" 를 포함하는
g : 전역 (플래그)

+ : 최소 한 개 or 여러개 /apple+/ 이 친구 덕분에 여러 개도 붙어서 나옴

    // 1. 정규표현식을 사용해서 my_string의 숫자 부분을 발라낸다.
    // my_string이 "aAb1B2cC34oOp" 이라면 nums = [ '1', '2', '34' ]
    
	const nums = my_string.match([0-9]+/g);
    
    return nums.map(num => Number(num)).reduce( (a, c) => a + c, 0)


한 번만 등장한 문자 indexOf, lastIndexOf

function solution(s) {
    ans = [];
    s = s.split('')
    
    s.forEach((item) => {
        // 앞에서 부터 찾은 값과 뒤에서 부터 찾은 값이 같다는 것은 배열에서 "한 번만" 등장한다는 것
        if(s.indexOf(item) === s.lastIndexOf(item)){
            // 결과로 반환할 배열에 넣어주기
            ans.push(item);
        }
    })
    
    // 배열을 소팅한 다음에 문자열로 변환
    return ans.sort().join("");
}

이진수 더하기 (parseInt(이진수, 2) & toString(2))

우선, 2진수를 10진수로 바꾸는 문법과 10진수를 2진수로 바꾸는 문법을 알고 있어야함

이진수 -> 십진수

parseInt(이진수, 2)

십진수 -> 이진수

십진수.toString(2)

이진수 10를 십진수 4로 바꾸고 이진수 11를 6으로 바꾼 후
두 개를 더한 십진수 10을 다시 이진수로 나타내면 됨


컨트롤 제트 for (let v of 배열)

스택을 생각한다면 쉬운 문제
반복문을 통해 배열에 있는 것들을 하나씩 넣다가 Z가 나온다면
만들어져있던 스택에서 한 개를 POP하고 그렇지 않으면 계속 넣어주면 됨

function solution(s) {
	// 공백을 한 칸으로 띄워져 있기 때문에 split을 해줄 때 공백한 칸을 줘야함 
	s = s.split(" ")
    	
    // 스택으로 쓸 배열 
   	res = [] ;
    
    // res배열에서 v를 한 개 씩 꺼내올 것
    // 넣을 차례가 Z라면 채우던 res에서 한 개를 빼즘, 단 여기서 에러방지용으로
    // res의 길이가 1이상일 때만 허용 
    for (let v of s) v === "Z" ? (res.length > 0 ? res.pop() : "") : res.push(v)
    
    // ★ 여기에서 a+ +c를 해주는 이유는 음수를 더해야하기 때문에
    return res.reduce( (a, c) => a+ +c, 0)
    
}
다른사람 풀이


forEach를 활용하여 한 개씩 꺼내옴 여기서도 더해줄 떄 +target을 통해 음수도 넣어주는 것을 볼 수 있음


소인수 분해

문제는 간단하게 2로 나누다가 나머지가 있으면 div의 값을 1씩 올리는 것

처음에 나눠주는 숫자를 2로 지정하여 변수로 만들고
for문 대신 while문을 활용 (for문을 i값의 증가가 번거로우니까)

function solution(n){
	ans = [];
    div = 2;
    
    while( n >= 2) {
      if (n % div === 0) {
          res.push(div)
          n = n/div
      } else {
          div++
      }
	}
    
    // 전개연산자를 통한 set변환 가능
    return [...new Set(res)]
}

영어가 싫어요 forEach

영어숫자의 순서대로 인덱스에 배정해준 배열(strs)을 만든 다음

그 배열의 각 값들을 가지고 strs에서 찾아 replace을 통한 변경

function solution(numbers) {
    // 숫자를 str의 인덱스순으로 맞추어 놔야 인덱스를 해당하는 문자 찾아 바꿈
    let strs = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']
    
    // numbers에서 해당하는 str (영어숫자)를 strs에 존재하는 인덱스로 변경
    strs.forEach( (str,i) => {
        numbers = numbers.replaceAll(str, i)
    })
    
    return Number(numbers)
}
다른사람의 풀이

객체에 값을 담고 replace와 정규표현식을 활용한 풀이
replace도 콜백함수가 가능하다는 걸 잊지 말기

function solution(numbers) {
    const obj = {
        zero: 0, one: 1, two: 2, three: 3, four: 4,
        five: 5, six: 6, seven: 7, eight: 8, nine: 9
    };

    const num = numbers.replace(/zero|one|two|three|four|five|six|seven|eight|nine/g, (v) => {
        return obj[v];
    });

    return Number(num);
}

문자열 계산하기 eval

eval을 사용하면 되지만 eval은 evil임

반복문을 통하여 각 인덱스를 검사하고 인덱스에 연산자에 따라서 answer라는 정수 변수에 더해주거나 빼주면 됨

function solution(my_string) {
	my_string = mystring.split(' ');
    answer = 0;
    // 연산자 초기 지정으로 삼항연산자에 활용
    operator = '+';
    
    my_string.forEach( (v, i) => {
    	// 배열을 순회하며 숫자가 아닌 문자가 나왔을 때 그 값을 연산자에 지정 
    	if (isNaN(Number(my_string[i]))) {
        	operator = my_string[i];
		} else {
        	// 연산자에 따라 answer에 연산을 실행
        	operator === '-' ? answer -= parseInt(my_string[i]) : answer += parseInt(my_string[i]);
		}
    })
    return answer
}

parseInt와 Number차이

let test = '10.12345';
parseInt(test); // 10
Number(test); // 10.12345

let test = '2020년도';
parseInt(test); // 2020
Number(test); // NaN

parseInt는 문자열로 된 부분에서 숫자만을 뽑아서 변환
Number는 문자열 전체가숫자일 때만 사용 가능


잘라서 배열로 저장하기

[...배열].splice가 아닌 배열.splice와 slice는 원본 배열에 영향을 미친다는 것을 활용하면 쉬움

substr은 원본에 영향 X

function solution(my_str, n) {
	ans = [] ;
    my_str = my_str.split("")
    
    while(my_str.length > 0) {
    	// splice로 분해하면 각각이 한 배열에 들어가니까 join으로 뭉쳐주곤 push
    	ans.push(my_str.splice(0, n).join(''))
    }
    return ans

}

// 다른사람 풀이
물론 for문과 substr (몇 개씩 뺸다) 를 활용해도 가능
function solution(my_str, n) {
    var answer = [];
    for(let i=0; i < my_str.length; i+=n) {
        answer.push(my_str.substr(i, n));
    }
    return answer;
}

substr과 substring의 차이

substr은 앞 인덱스부터 몇 개를 빼겠다는 것이고
substring은 앞 인덱스부터 뒤 인덱스-1 까지 뺴겠다는 것


구슬을 나누는 경우의 수 factorial, round


팩토리얼을 구현해서 매우 쉽게 푼 문제 3C2를 계산하면 된다

function fac(n) {
	if ( n <= 1) {
    	return 1
    } else {
    	n * fac(n-1)
	}
}

function solution(balls, share) {
	k = balls - share
    // 소수점 30!/29! 인 경우 30.000000....1 같은 값이 나오기 때문에 반올림 
    return Math.roud(fac(balls)/(fac(k)*fac(share))
}

삼각형의 완성조건 (2) Math.max(...배열)

수학을 하도 손 뗐어서 조건이 기억 안 나던 문제지만
함수로 구현해서 풀었다


외계어 사전 every, some

every - 배열의 모든 요소가 callbackFunction 에서 true를 리턴해야 true를 리턴,
하나라도 false가 떨어지면 false를 리턴

some - 배열의 요소 중 하나라도 callbackFunction에서 true를 리턴하면 true를 리턴

function solution(spell, dic) {
    // spell에 해당하는 값들을 모두 갖고 있는 애가 있으면 걔를 배열에 담아 리턴 
    // includes를 통해 매치되는 애가 없으면 배열에 아무것도 들어가지 않으니 길이를 통해 삼항연산자
    return dic.filter( v => spell.every( c => v.includes(c) )).length ? 1 : 2
}

로그인 성공 Map자료구조


나는 덕지덕지 조건문을 활용하여 풀었지만.. (일전에 프로젝트에서 로그인창에 썼던 로직)

여러가지 신박한 풀이법이 많다

1. Map자료구조 활용

Map은 객체와 비슷하지만 한 가지 다른 점은 키에 여러가지 자료형이 들어갈 수 있음

Map

new Map() – 맵을 만듭니다.
map.set(key, value) – key를 이용해 value를 저장합니다.
map.get(key) – key에 해당하는 값을 반환합니다. key가 존재하지 않으면 undefined를 반환합니다.
map.has(key) – key가 존재하면 true, 존재하지 않으면 false를 반환합니다.
map.delete(key) – key에 해당하는 값을 삭제합니다.
map.clear() – 맵 안의 모든 요소를 제거합니다.
map.size – 요소의 개수를 반환합니다.

1. 맵을 이용한 풀이
function solution(id_pw, db) {
  const [id, pw] = id_pw;
  const map = new Map(db);
  return map.has(id) ? (map.get(id) === pw ? 'login' : 'wrong pw') : 'fail';
}

2. filter를 활용해서 아이디가 있는 애들만 모아둔 것

function solution(id_pw, db) {
    db = db.filter(v=>v[0]===id_pw[0]);

    if (!db.length) return 'fail';

    for (let d of db) if (d[1] === id_pw[1]) return 'login';

    return 'wrong pw';
}

치킨 쿠폰 Math.floor() = ~~ 연산자

function solution(chicken) {
    // 쿠폰 = 치킨 수
    let coupon = chicken
    
    // 서비스 받은 치킨 수
    let serviceChicken = 0
    
    // 쿠폰이 10개 이상이라면
    while(coupon >= 10) {
        
        // 쿠폰을 10으로 나눈 몫만큼 서비스 치킨 추가
        serviceChicken += Math.floor(coupon/10)

        // 여기에서 쿠폰을 재귀적으로 더해줌
        // 남은 쿠폰 = coupon%10, "쿠폰을 통해 주문한 치킨의 쿠폰 수" = coupon/10
        coupon = coupon%10 + Math.floor(coupon/10)
    }
    
    return serviceChicken
}

★ 등수 매기기 indexOf

indexOf(찾을값)는 배열에서 값들 중 가장 처음에 찾은 인덱스를 반환하는 것을 잊지 않으면 동점자도 쉽게 해결할 수 있다.

ex)
스코어 평균 75 70 55 65 55
내림차순 75 70 65 55 55
을 통해서 찾은 애의 인덱스값에 +1 (인덱스가 0부터이니)

function solution(score) {
    
    // 내림차순을 통해 점수가 클수록 인덱스가 앞으로 오게 한다. 
    // indexOf는 찾은 인덱스 중 가장 첫번째를 반환하기 때문에
    // 앞에 있는 애부터 +1씩 해주면 됨
    
    // 1. score의 평균 점수를 구한다.
  	let avg = score.map(v=>(v[0]+v[1])/2);
  	
  	// 2. avg를 내림차순으로 정렬한 배열 sort를 선언한다.
    
    let sorted = avg.slice().sort((a,b)=> b-a);
  	
  	// 3. avg의 요소와 동일한 sorted 배열의 index 값에 1을 더한다.
    return avg.map(v => sorted.indexOf(v) + 1);
}

배열 교집합 filter & includes

let arrA = [1, 4, 3, 2]; 
let arrB = [5, 2, 6, 7, 1]; 

arrA.filter(it => arrB.includes(it)); // returns [1, 2]

유한소수 판별하기


처음엔 약수를 구하는 함수를 만들어서 약수를 구한 다음
둘의 중첩되는 리스트를 만들어서 pop으로 최댓값을 빼서 썼지만

최대공약수라는 이유로 둘의 공약수를 모두 구할 필요없이 분모값을 활용한 for문을 통해 a와 b 둘 다 나머지가 0으로 만드는 수를 구해서 넣어줌

그후 while문 두 개를 통해 b의 값이 2나 5로 계속 나누어서 1이 되는지 확인하고
1이 됐다면 return 하는 방식을 참고하여 풀었다

function solution(a, b) {

    maxNum = 0;
    for (let i = 1; i<= b; i++){
        if(a%i ===0 && b%i ===0) {
           maxNum = i;
        }
    }
	
 
    // 분모를 최대공약수로 나누어서 기약분수로 만듦
    b = b/maxNum

    while (b%2 === 0) b = b/2
    while (b%5 === 0) b = b/5

    // 기약분수의 소인수가 2와 5뿐이여야함
    return b===1 ? 1 : 2 
}

// function getNum(number) {
//     res = [];
//     div = 1;

//     for(i=1; i<= number ; i++) {
//         if (number % i === 0) {
//             res.push(i)            
//         }
//     }

//     return res
// }

저주의 숫자 3

3의 배수가 아닌 숫자들을 다 넣어준 다음
배열에서 String을 통한 형변환 + .includes('3') 을 통해
3을 가진 친구들을 다 제거한 후 n을 인덱스값으로 받아서 하면 되는데

인덱스값을 꺼내야하므로, n-1

function solution(n) {
    ans = []
    for (i=1; i<200; i++) {
        if(i % 3 !==0) {
            ans.push(i)
        } 
    }
    
    ans = ans.filter( (v,i) => !String(v).includes('3'))
    
    return ans[n-1]
}

다른사람 풀이

function solution(n) {
    var answer = 0;
    
    // 3의 배수에 해당하면 n의 값을 증가시킴 (건너뛴다는 소리)
    // 자연스럽게 for문속 n도 함꼐 증가
    for(let i=1; i<=n; i++){
        if(i%3 == 0){
            n++;
        }
        // 3을 포함하고 있는데 3으로 깔끔히 나눠지지않으면 n을 증가시킴
        // 33 같은 애들은 이미 3의 배수에서 제외해줬으니
        if(String(i).includes("3")& i%3 != 0){
            n++
        }
    }
    return n;
}

★★ 특이한 정렬 sort

sort의 콜백함수를 고쳐서 푸는 문제

function solution(numlist, n) {
    return numlist.sort((a,b) => {
    
        // 각 인덱스와 n의 거리차이 계산
        const [aGab, bGab] = [Math.abs(a-n), Math.abs(b-n)]
        
        // 거리가 같다면 더 큰 수를 우선 배치
        if(aGab === bGab) return b-a
        
        // 다르다면 거리별 오름차순 정렬
        return aGab-bGab
    })
}

다른 사람 풀이

function solution(numlist, n) {
	// 내림차순으로 먼저 정렬
    // 그 후 각 값들에서 n을 빼준 값이 작은 순으로 다시 오름차순 정렬 
    return numlist.sort((a,b)=>b-a).sort((a,b)=>Math.abs(a-n)-Math.abs(b-n))
}

문자열 밀기

문자열을 밀어서 최소로 되는 횟수와 안 되면 -1, A와 B가 같으면 0

그냥 정석대로 푼 느낌이다.
function solution(A, B) {
    ANS = A.split('')
    count = 0;

    while(ANS.join('') !== B) {
        ANS.unshift(ANS.pop())
        count+=1

        if(count > A.length) return -1
    }


    return count ? count : (A===B &&  0) 
}

신기한 다른 사람 풀이

let solution = (A,N) => (B+B).indexOf(A)

B랑 B를 더하면
"ohellohell"
이렇게 되는데 여기서 문자열 A가 시작하는 인덱스 즉, 1을 반환
존재하지 않는다면 당연히 -1이 리턴될 것이고
처음부터 같다면 0이 리턴.. 캬
profile
기록, 꺼내 쓸 수 있는 즐거움

0개의 댓글