아직 까지는 레벨 2중에 쉬운 문제가 아닐까?

 

하는 생각이 들지만, 놀랍게도 결국 혼자 풀기엔 어려움이 있던 문제였다.

 

 

문제를 간단히 해석해 보면, 

N 명으로 시작하여, 

라운드 (answer) 가 지날때 마다 인원수는 절반이 될것이다. 

만약, A 와 B가 첫 번째 라운드에서 붙는다면, answer = 1 이 될것이기 때문에

N/1 = 1 이 될 것이고,

 

이후에 진행될 대전 상황은,

 

N/2 = 2

N/4 = 3

N/8 = 4 ... 이런 느낌이 될 것이다 .   

 

N/8 은 N/N 이므로, 1명만 남게 된 상황이고 우승까지 필요한 라운드가 answer= 4 가 될 것이다..

 

 

정리해보면,

 

N 부터 시작해서 answer =1

다음 라운드 = N/2^1  , answer ++  와 같이 진행될 것이고,

A 와 B 는 반드시 이긴다고 가정하고, N 은 항상 2의 배수 이며, 

A 와 B가 짝수라고 가정하면 A와 B를 2로 나눌때 마다 라운드가 1회 진행된다고 볼 수 있다.

 

 

그렇게 했을 때, a와 b의 차이가 1이 나게 된다면, a 와 b가 붙는 상황이므로

위처럼 구면되지 않을까 ?

 

하지만 실패와 시간 초과가 무수히 발생

 

이유는, 짝수로 만들고 시작한다고 해도, 한 쪽이 1이 되어버린 이후

계속해서 while문이 반복된다면 0.5... 0.25 .. 계속 소수로 작아질 것이기 때문에

무한 루프가 완성된다.

 

 

코드를 위와 같이 개선하여, 굳이 짝수로 맞춰주지 않고

Mah.ceil 을 통해 결과값이 홀수면 올림처리를 하여 반드시 짝수가 되도록 했다.

A 나 B가 이미 짝수라면 변화가 없고, 홀수라면 결과값이 1 증가하여 짝수가 되기 때문에 

코드가 간단해졌고, 결과값이 1 / 2 = 0.5 가 된 경우도 올림처리를 하여 1이 되기 때문에 무한루프 문제도 해결됐다.

하지만 또 에러 발생

위 처럼 초기에 1차이가 나는 상태로 시작할 경우, 

반복문이 실행되지 않고 그대로 answer =1 을 반환해버린다.

 

아무래도 조건을 다르게 설정해야 할 듯 하다.

 

 

위처럼  answer = 0으로 시작하여, 

a 와 b 가 같지 않을경우 반복문이 실행되게 한다면

위에서 나타난 문제점을 전부 해결할 수 있다.

 

 

조건에 A !== B 를 명시했기 때문에 a=b 로 시작해 0이 반환될 일도 없다.

 

 

 

프로그래머스의  2레벨이 시작되었다...

 

2레벨 초입이라 그런지, 풀이가 어느정도 가능할 것으로 보이는 문제이다.

 

 

n은 2 이상으로 주어지기 때문에 시작을 2로 잡아보았다.

 

 

이렇게 배열을 하나 만들어서  Fn-2 Fn-1 Fn 을 표현해 주었다.

 

first[2] 를 반환하여 정답을 만드는것

 

 

문제 그대로를 집어넣은듯한 반복문을 작성해주었다.

 

1회 반복될 때 마다,

first[1],first[2] =>  first[0] first[1] 이 되고

first[2] = first[0] +first[1] 로 갱신되는 형태

  

 

shift를 사용해서 배열의 값을 한칸 씩 땡기고 first[2] 를 다시 만들어 주는것도 같은 결과일 것이다.

shift() 의 시간복잡도는  O(n) 인데,  이 경우 항상 length =3 에서 진행되기 때문에 3의 시간 복잡도가 된다.

 

하지만 first[0]  과  firtst[1] 을 각각 수정하면  복잡도가 1+1 이기 때문에  n당 n회 이득이다..ㅋㅋ...

 

크헉

 

 

 

조건이 더 있었다.

Int 한계 때문에 생긴 조건일까?

 

 

마찬가지로 조건도 그대로 넣어버린다

 

 

 

 

 

지금 타이밍에 풀기 좋은 문제가 등장 !

 

 

겉보기에는 엄청 쉬운 문제인것 처럼 느껴지지만, 이 문제의 핵심은

 

 

문제에서 주어지는 today, terms, privacies 가 정제되지 않는 형태로 주어진다.

완전 쌩 문자열 이라는것이다.

 

그러나 나는 주어진 문자열을 원하는 형태로 가공하는것에 약하다 !

일단 처음 계획은

date 에 해당하는 부분은 숫자열로 바꿔준다 !

 

terms 이 문제인데, 아무래도 객체 형태로 만드는것이 좋을것 같다 !

{ A : 6 , B : 12 , C: 3 }  이렇게 만들어주면 !

키값을 참고하여 숫자를 어떻게 해먹을 수 있겠다.

 

기초적인 내용이라고 생각되지만...

어째선지, 잘 모르기 때문에 일단 킹st.js 로 !!

 

 

 

가장 기초적인 객체의 사용방법 !

object .  key = value   로, 한번에 빈 객체에 key 와 value를 집어넣을 수 있다 .

 

obj[key] = value 형태를 통해, 원하는 key값을 변수를 사용해 넣어줄 수 있다. 

 

다시 문제로 돌아와서, terms 를 { A : 6 , B : 12 , C: 3 } 로 만들어보자.

 

이렇게 하면

{ A : 6 , B : 12 , C: 3 }  를 만들수  .. 가 없다

 

terms 의 값이 전부 문자열이기 때문이다.

+를 사용하던지, Numer(term)  을 해주던지 하여 숫자열로 변환후 value에 넣어주자.

 

 

그 다음은, 날짜를 계산해야 하는데, 아주 원시적인 방법을 사용하기로 했다. 

일단은 말이다 !!

보기만 해도 점수가 깎여나갈것 같은 방법이지만 당장은 가능하다는게 중요

month + year*12 + day/100  이것으로 날짜를 비교할 수 있게 되었다..(흠흠)

 

 이제, 두 방법을 활용 하여

문제에서 제시한 privacies 를 흠흠↑ 의 형태로 바꾸어 비교하기만 하면된다.

 

 

방법은 사실 상 동일하다. 

privacies의 앞부분인 2021.05.02 를 위와 같은 이상한 소수점으로 만들고

여기에 A를  앞에서만든 termType에서 찾아서 value를 더해주면 된다. 

 

그리고 반복문을 통해, 값을 비교해주면 끝 .. !

 

 

 

 

 

문제가 살짝 난해한 내용이다.

 

요점은, #이 표시된 위치에 따라 드래그 할 영역을 정해야 한다는 것.

 

 

정답을 구하는 방법 자체는 금방 떠올랐다. 

 

이제 답을 구하러 가야하는데,

늘 그랬던 것처럼 for반복을 하기로 했다. 

 

 

우리가 필요한건 wallpaper[i] 에  #이 포함된 경우와 wallpaper[i][j] 가 # 인 경우

그중 가장 높은값과 낮은값이 필요하다. 

 

제한사항 자체가 그렇게 빡세지는 않기 때문에 for 기본 반복문을 통해서 해당 값을 구하고, 

answer에 담아 리턴해주면 끝 !

 

 

 

문제가 너무 길기 때문에, 자세한 내용은 생략한다

 

문제가 길지만, 읽어보니 특별한 메소드가 필요하거나 하진 않을 것 같다.

 

우선, 점수를 카운트하기 위해 객체를 만들어 준다. 

 

그 다음, 점수를 얻는 과정을 반복문으로 작성해보자.

 

수준이 좀 떨어져 보이지만, 일단 생각난 그대로 적어 보았다.

짜치긴 해도, 추가 설명이 필요 없을 정도로 직관적인 코드가 아닐까 !!

 

위 의 반복문을 통해서 점수를 기록했으니, 이제 결과를 만들어야한다.

 

 

짜잔!..........

 

 

 

function solution(survey, choices) {
    var answer = '';
// 각 지표에 해당하는 성격 유형 점수표가 필요함
    let score = { R: 0, T:0, C:0, F:0, J:0, M:0, A:0, N:0}
    
    for(let i = 0; i < choices.length ; i++) {
     const [left, right] = survey[i].split("");
        if (choices[i] > 4) {
            score[right] += choices[i]-4
        } else if ( choices[i] === 1) {
            score[left] += 3
        } else if ( choices[i] === 2) {
            score[left] += 2
        } else if ( choices[i] === 3) {
            score[left] += 1
        }        
        }

    if ( score.R >= score.T) {
        answer += "R"
    } else answer += "T"
    if ( score.C >= score.F) {
        answer += "C"
    } else answer += "F"
    if ( score.J >= score.M) {
        answer += "J"
    } else answer += "M"
    if ( score.A >= score.N) {
        answer += "A"
    } else answer += "N"
    return answer;
}

 

정말 짜친다는 말이 절로 나오는 코드이지만 , 큰 하자없이 잘 작동하는 상태이고,

여유가 될 때에 코드를 가다듬으러 다시 이 문제로 돌아와야겠다.

 

 

 

햄버거 만들기

햄버거는 쌓아서 만든다는 특징이 있다... 쌓는다 !

 

이 문제는, stack 의 풀이를 사용할 것을 유도하고 있다.

 

 last in , first Out 

마지막에 들어온 데이터가 가장 먼저 빠져온다. 

그렇기 때문에, 스택 구조는 시간 복잡도 면에서 유리하다.

데이터가 위쪽에 쌓이는 형태이기 때문에 PUSH (O)n 

나가는 것도 맨 위쪽이기 때문에 POP (O)n 이기 때문이다.

 

 

 

우선 stack 배열을 만들고, 

주어진 배열 ingredient 를 순회하며 앞에서 부터 하나씩 쌓는다. Stack !

 

 

하나쌓을 때 마다, stack 배열의  뒤에서부터 4개의 값을 조사한다. 

1231 일 경우, 햄버거를 만들 수 있으므로, answer ++ 를 해주고,

조립한 햄버거 재료를 제외한다.

다시 반복문으로 돌아가 다음 재료 stack에 추가한다 ... 반복

 

이렇게 하면, 문제의 핵심인

 

1231이 아니어서 지나친 재료가  다시 1231의 재료가 될 수 있는 부분 

12  1231 31    << 와 같은 경우 또한 정상적으로 조립 대상에 포함할 수 있다.

12 1231 에서 answer ++  후 1231 제거

12 3 

12 3 1  에서 answer ++  후 1231 제거

 

 

 

 

 

코드 카타에 사용할 이미지

 

 

오랜만에 못풀진 않을듯 ? 하는 문제가 나왔다.

 

문제의 포인트는 2가지로 설명할 수 있다.

 

1. 소문자 알파벳으로 이루어진 문자열 s가 , index 의 int만큼 밀려서 다음 알파벳으로 출력됨

=> 이는 아스키 코드 의 문제와 같은 부류이고,

 

2. skip 내에 있는 알파벳은 기본적으로 건너 뛰게 된다.

=> 이 부분이 해당 문제의 난이도를 올리는 부분이다.

 

우선, 아스키 코드 대신 저번처럼 알파벳 문자열과 index를 사용하기로 했다.

 

소문자만 존재하므로, 

 

전체 소문자 문자열을 arr에 할당해주고,

(왜 변수명을 arr로 했지?)

 

반복문을 통해 arr배열.. 아 아니라 문자열에서 s[i] 에 해당하는 알파벳의 인덱스를 뽑아오면, 

s와 동일한 값을 우선 추출할 수 있다.

s.length 만큼 반복시 s와 동일한 값이 나오게 됨

 

문제는  skip 부분인데, 문자열 arr 을 만들면서 생각이 난게 있다. 

skip에 해당하는 알파벳을 다 없애버리면 그게 skip이 아닐까?

 

skip 에 해당하는 알파벳은 조건없이 항상 건너 뛰게 되므로, 사실상 없는것과 같다.

 

 

프로그래머스 치고,  상당히 친절한 조건까지 붙어있다.

 

 

 

반복문과 replace 메서드 를 이용해 하나하나 날려주기로 했다.

뭔가 한번에 날릴 방법이 없을까 생각했지만, 찾아내진 못했다.

 

skip 에 해당하는 문자가 도려내졌다. 

 

이제, 위의 방법에서 주어진 index만큼을 더해서 반환하면 된...

 

 

 

당연히 안된다

 

z 이후를 참조하려 하면 undefined 이기 때문이다  

 

 

arr.length 만큼 나눠서, 나머지에 해당하는 부분으로 적용하면,

arr.length 이하인 경우에 대해서는 기존처럼 작동하고,

넘어가면 자연스럽게 0부터 시작하는 형태가 완성된다.

 

 

 

 

 

매일 1시간씩 코드 카타 시간을 갖는데, 이 문제를 푸는데 3일이 소요됐다.

 

문제를 이해하고, 어떤 방법으로 구현할까 생각했는데

 

keymap 에 해당되는 문자열을

 

{A : 1, B:2, C: 3} 과 같은 형태로 만들어 키와 밸류를 이용해 문제를 풀어나가면 

될것 같다는 생각을 했다.

 

검색을 통해 알아 본 결과, 위와 같이 배열의 데이터를 

키와 밸류로 각각 나누어 객체 형태로 저장하는 방법을 해시(Hash) 라고 한다 !

 

배열과의 차이는,

 

배열은

0~ 으로 시작하는, 순번을 알려주는 index 와  index의 값인 요소(element) 로 구성된다면,

 

객체는

임의의 key : 임의의 값 value  로 구성이 되기 때문에, 

 key 에 해당하는 부분에 숫자는 물론 문자열 또한 들어갈 수 있기 때문에 자유롭다 !

 

이 둘의 차이는 마치,

관계형 데이터 베이스 (배열) 와 비관계형 데이터 베이스 (객체) 와 비슷하다고 생각된다.

 

일반적으로 JS 에서는 해시 형태를 만들기 위해 그의 단짝인 Map이 사용된다.

 

 

 

라고 하는 내용을 찾을 수 있었으나, 

 

한번에 이해하고 사용하기엔 어려웠기 때문에,

일단은 원시적인 방법으로 해시를 구현해보고자 했다.

 

 

여기까지 오기가 가장 힘들었다

 

객체로 선언된 keykey에, 이중 반복문을 통해

key에 해당하는 keymap의 문자열, value에 해당 문자열의 index 를 넣는 방식으로 만들었다.

 

이것은 사실상, map() 이 작동되는 방식을 반복문으로 표현한 것과 같다.

 

이제, targets 을 순회하며 일치하는 값을 더해주기만 하면 된다.

 

 

마찬가지로, 이중 반복문을 통해 몇 번째 index 에 있는 몇 번째 문자열인지를 참고하여

순회한다. 만약 key에 없는 문자를 한번이라도 마주친다면,

작성이 불가능한 문자열 이므로 -1을 반환하고 다음 i로 넘어간다.

 

이 때, break 를 하지 않는다면

sum= -1 이 되었지만  남은 j 반복문을 계속 진행하여 -1 에서 값이 변경될 여지가 생기게된다.

 

이 부분을 놓쳐서 꽤 긴 시간을 낭비했다.

( 기본으로 주어지는 테스트 케이스 에서는, sum= -1 이후에 변동되는 케이스가 없기 때문에 )

 

이를테면,

임의로 추가한 위의 테스트 케이스 에서,

2번째 targets 배열인 DADFA 를 보면, 

F가 작성이 불가능한 문자열 이므로 sum = -1 이 되었지만, break 가 없다면

F 다음 index인  A로 넘어가면서 A= +1 의 값을 받아 sum= 0 이 되어버리는 문제가 생기는 것이었다.

 

 

break ;  적용 이후로, 실패했던 테스트가 통과되며 정답처리가 되었다. 

 

 

상당히 골치아픈 문제가 나와서 글로 작성해 보았다.

이게 level 1 이라니...

 

얼핏보면 수월하게 풀 수 있을것 같은 문제지만 (그렇게 생각했었는데요...)

제한 사항을 보면 X와 Y의 길이가 최대 300만 으로 정해져 있으며,

이로 인해서 이중 for문 등을 사용하면 시간복잡도가 급증해 시간초과의 결과가 나오게 된다.

 

 

풀이 설계

 

1. X 와 Y 를 내림차 정렬을 한다.

2. X 와 Y를 맨 앞에서부터 비교하여 같을 경우 answer 에 추가

3. X[0] Y[0] 이 같지 않을 경우 둘 중 작은 값의 다음 index를 검사

4. "0" 의 결과와 "-1" 의 결과를 따로 리턴

 

늘 그렇지만, 이번에도 대단히 효율적이진 않을것 같은 풀이 방향이다.

내림차 정렬을 위한 sort 2회, X와 Y의 값을 비교하기 위한 반복문이 기본적으로 필요하기 때문.

 

 

풀이 시작

 

    X= [...X].sort((a,b) => b-a)
    Y= [...Y].sort((a,b) => b-a)

시작은 역순정렬 부터 한다. 큰 값으로 정렬하여 비교하면 유리할 거라는 생각이었으나,

사실, sort 과정 자체가 시간복잡도를 많이 잡아먹어버린다.

어쨌든 그렇게 설계했기 때문에 이렇게 시작해 보도록 하자.

 

이 과정에서 저번 알고리즘 때 배웠던 [...str] 을 이용한 배열화를 사용해 보았다.

 

그 후, X와 Y를 비교하기 위해 반복문을 사용해야 하는데, 

while 문을 사용하기로 했다. 

 

    let i = 0;
    let j = 0;
    while ( X.length > i && Y.length > j) {
        if ( X[i] === Y[j]) {
            answer += X[i]
            j++ 
            i++
        }

X 와 Y의 끝까지 비교를 해야할 것으로 예상되기 때문에, 위 같이 조건을 주었고,

가장 중요한 X[i] ===Y[j] 일 경우 answer에 추가하는 것 까지는 어렵지 않게 진행되었다.

 

 

잘못된 풀이..?

그 전에, 잠깐 뻘짓을 했던 과정을 살펴보자면

    while ( X.length > i && Y.length > j) {
        if ( X[0] === Y[0]) {
            answer += X[0]
            X= X.substring(1)
            Y= Y.substring(1)
        }

위처럼 동일한 조건으로 진행하지만, i j 를 통해 다음 index로 넘어가는것이 아니라,

.substirng(1) 을 통해  X,Y의 0번의 index만 검사하도록 진행해 보려는 시도를 했었는데, 

굳이 substring 이라는 메서드가 사용된다는 점 때문에 폐기 되었다.

 

시도했던 이유와 폐기한 이유??

 

substring 은 문자열의 0번째 ~를 제거하는 메서드이다. 

참조형이 아닌 기본형 타입의 string을 변조하는건 시간복잡도가 1회 일 것으로 예상을 했었으나,

기본적으로 substring() 의 시간 복잡도는 O(n) 에 해당했다.

글 마무리에 substring을 사용했을 때와, index 순번을 사용했을 때의 시간차이를 비교해보도록 하겠다.

 

 

자, 다시 풀이로 돌아와서, 이제 X[i] 와 Y[j] 가 다른 값일 경우만 처리하면 된다.

 

생각이 닿기는 좀 시간이 걸렸지만,

    while ( X.length > i && Y.length > j) {
        if ( X[i] === Y[j]) {
            answer += X[i]
            j++ 
            i++
        } else if (X[i] > Y[j]){
            i++          
        } else { 
            j++}
        
    }

X[i] 와 Y[j] 의  둘 중 큰 값을 비교해서 

큰 값을 가진쪽의 index 를++ 해서  다음 index를 참조하게 하면,

점점 값이 작아지며 서로 같은 값이 나올때 까지 넘어가게 된다.

 

마무리로, 

    if (answer[0] === "0" ) { return "0"}
    return answer === ""? "-1" : answer

"000000" 만 나온 경우와  일치하는값이 하나도 없을경우만 따로 빼주면 정답이 완성된다.

 

답 코드

function solution(X, Y) {
    var answer = '';
    
    X= [...X].sort((a,b) => b-a)
    Y= [...Y].sort((a,b) => b-a)
    let i = 0;
    let j = 0;
    while ( X.length > i && Y.length > j) {
        if ( X[i] === Y[j]) {
            answer += X[i]
            j++ 
            i++
        } else if (X[i] > Y[j]){
            i++          
        } else { 
            j++}
        
    }
    if (answer[0] === "0" ) { return "0"}
    return answer === ""? "-1" : answer
}

 앞서 말한것 처럼, sort와 반복문이 사용되며 결론적으로 그렇게 효율적인 코드는 아니게 되었다.

 

딱 봐도  11~ 15번 case 가 헬 구간 ( X,Y length가 길게 주어지는듯)

 

 

 

 

 

그렇다면, substirng 의 경우 얼마나 차이가 날까?

function solution(X, Y) {
    var answer = '';
    
    X= [...X].sort((a,b) => b-a).join('')
    Y= [...Y].sort((a,b) => b-a).join('')
    while ( X.length > 0 && Y.length > 0) {
        if ( X[0] === Y[0]) {
            answer += X[0]
            X= X.substring(1)
            Y= Y.substring(1)
        } else if (X[0] > Y[0]){
            X= X.substring(1)       
        } else {
            Y= Y.substring(1)}
        
    }
    if (answer[0] === "0" ) { return "0"}
    return answer === ""? "-1" : answer
}

.substirng(1) 이기 때문에 별로 차이가 없지 않을까 하는 생각에서 완성을 해 보았다.

 

 

유의미한 차이가 있다.

substirng(1) 이라고 해도 결코 상수의 처리시간이 걸리는건 아닌것으로 보인다.

물론 X 와 Y를 추가로 join 해주는 과정에서도 추가적인 시간이 소요됐겠지만 말이다.

 

대충 정리를 하자면 

11~15번 케이스에서 걸린 시간은 

기본 답 코드 ( X[i] Y[j] 로 비교) = 약 2300ms 가량 

두 번째 테스트 ( 기본 답 코드에서 X,Y를 join만 해준 경우)  = 약 2500ms 가량 (join에 걸리는 시간 대략적으로 확인)

세 번째 테스트 ( join 이후 substring을 통해 X[0] Y[0] 으로 비교) =  약 2800ms 가량 ( join과 substring에 걸리는 시간 확인)

 

 

당연히~~~~~

X[i] Y[j] 로 비교하는것이 빠르겠지만, 순전히 궁금증이 생겨나서 

테스트를 해 보게 되었다.

결론은.... 애초에 sort 가 제일 오래 걸리는듯 함 (ㅠ)

 

 

+ Recent posts