1. 문제 파악하기

- orders 배열의 크기는 2 이상 20이하이다. & orders 배열의 각 원소의 크기는 2이상 10이하인 문자열이며 대문자로만 이루어져 있다.

- 정답은 오름차순으로 정렬해서 return해야함.

2. 문제 접근하기

- orders에 있는 원소들의 조합을 생각해야함. 이때 조합의 길이는 2이상 10이하의 자연수임 → itertools 라이브러리의 combinations를 사용할 것임

- 단품 메뉴 조합이 2번이상 나와야하며 가장 많이 나온 것들을 찾아야함.

 

3. 주의할 점

- orders의 원소들은 대문자로 되어있지만 사전순으로 정렬되어 있지 않음 → 먼저 정렬시킬 필요가 있음

- 언급은 되었더라도 2번 이상 언급이 된 코스요리여야함.

 

4. 정답 코드 및 해석

 

제가 작성한 코드는 아래와 같습니다.

from itertools import combinations

def solution(orders, course):
    answer = []
    for i in course :
        combination = []
        for j in orders :
            j = ''.join(sorted(list(j)))
            result = list(combinations(j,i))
            for k in result :
                combination.append((''.join(list(k))))
        result = []
        for j in combination :
            count = combination.count(j)
            result.append((j,count))
        result = list(set(result))
        result = sorted(result, key=lambda x: x[1], reverse=True)
        max_result = list(filter(lambda x: x[1] > 1 and x[1] == result[0][1], result))
        for j in max_result :
            answer.append(j[0])
    answer.sort()
    return answer

1. 우선 course의 원소를 기준으로 for문을 돕니다.

2. 그 안에 다른 for문을 작성하는데 이는 orders의 원소를 기준으로 돕니다. 이 for문의 용도는 orders 원소를 i길이로 combination하기 위함입니다. orders의 원소는 사전식으로 정렬되어 있지 않았습니다. 따라서 문자열을 리스트로 변형시키고 정렬한 다음 .join()을 이용해서 정렬된 문자열로 만듭니다.

3. result 변수 안에는 i길이로 조합된 원소들이 있습니다. 이때 result안에 원소들은 ("A","B")와 같이 집합 형태로 되어 있으므로 join()을 이용해서 combination이라는 변수에 조합된 문자열을 넣습니다. for문을 다 돌았을 때, combination 변수 안에는 orders안에 있는 원소가 i길이로 조합된 문자열이 들어 있을 것입니다. ex) i=2, orders = ["ABC", "EFQ"] 라면 combination의 최종 결과는 ["AB", "AC", "BC", "EF", "EQ", "FQ"]가 들어 있습니다.

4. 다시 for문을 만들어서 combination의 각 원소가 몇번 나왔는지 센다음 result에 (조합된 문자열, count) 형태로 저장합니다. 하지만 이때 result에는 중복된 값이 있을 수 있습니다. 그래서 result = list(set(result))를 이용해서 중복된 값을 제거한 다음 다시 리스트로 만듭니다.

5. result에 들어있는 원소들을 count를 기준으로 내림차순으로 정렬합니다. 그러면 가장 많이 count된 조합이 가장 앞에 있겠죠? count된 횟수가 같은 원소들이 있을테니 filter 함수를 이용해서 count가 2이상, 가장 많이 언급된 원소들만 골라냅니다. 골라낸 원소들은 max_result 변수에 저장됩니다.

6. filter된 원소들을 answer에 넣습니다.

7. 마지막 return하기전에 answer 리스트를 정렬한 다음 return합니다.

 

이렇게 코드를 작성하고 채점을 하면 다음과 같은 결과로 통과가 됩니다.

 

테스트 케이스 결과

 

5. 마치며

이번 문제는 level2치곤 쉬운 문제였습니다. 하지만 itertools의 combinations 기능을 몰랐다면 조합을 어떻게 만들지 조금 많이 고민했어야 했을거 같습니다. 이번 문제는 카카오 블라인드 채용에서 나온 문제인데 예전보다 카카오에서 나오는 문제들이 꽤 많이 까다롭고 복잡한 경우가 많은거 같습니다. 이번 문제는 코딩을 할줄 아냐 모르냐를 판별하는 정도인거 같은데 다른 문제들을 보면 꽤 어려운 문제들도 많더군요..ㅎ 더 노력해야겠습니다. 이번 문제는 조금 길어서 겁먹으신 분들도 있으실텐데 코딩테스트도 수능 문제처럼 문제가 길수록 힌트가 많은거 같습니다. 문제가 길수록 옆에 개인 노트에 잘 정리하면서 문제를 생각한다면 조금 더 수월하게 문제를 풀 수 있을거 같습니다.

1. 문제 파악하기

- prices의 각 가격은 1~10,000 이하인 자연수 & prices의 길이는 2이상 100,000 이하 → level 1에서보단 prices의 길이가 조금 긴편이지만 그래도 적당한 길이.

 

2. 문제 접근하기

- 초 단위로 기록된 주식 가격이 담긴 배열 prices가 매개변수로 주어지고, 가격이 떨어지지 않은 기간은 몇초인지 retrurn해야함. → 1초의 가격이 2이고 2초 가격이 1이면 return 배열에 들어가는 1초 가격이 떨어지지 않은 기간은 1초이다. 1초 가격이 2, 2초가격이 3, 3초 가격이 1이면 return 배열에 들어가는 1초 가격이 떨어지지 않은 기간은 2초이다.

 

3. 주의할 점

level 1 과 다르게 인자로 주어지는 값의 길이가 많이 커졌습니다. 이때 단순하게 level 1에서 생각한 방법으로 코드를 작성하시다간 시간초과가 일어날 가능성이 높습니다. 이번 문제에선 커트라인을 꽤 크게 주어진 듯하지만 개인적으로도 만족하지 않은 코드입니다. 다른 사람들 풀이의 댓글에보면 스택을 이용하면 O(n)로 문제를 풀 수 있다고 합니다. 저도 지금 당장은 생각이 나지도 않고 다른 사람들 풀이에도 O(n^2)으로 푼 방법 밖에 없어서 한번 생각해봐야겠습니다. ㅎㅎ

 

4. 정답 코드 및 해석

제가 문제를 푼 방법은 다음과 같습니다.

def solution(prices):
    answer = [0 for i in range(len(prices))]
    for i in range(len(prices)) :
        for j in range(i+1,len(prices)) :
            if prices[i] <= prices[j] :
                answer[i] += 1
            else :
                answer[i] += 1
                break
    return answer

시간 복잡도를 신경쓰지 않는다면 생각보다 간단하게 문제를 해결할 수 있습니다.

저는 우선 answer이라는 배열에 prices길이만큼 0이라는 숫자들을 담았습니다.

그런 다음 다들 보이시겠지만 2중 for문을 사용하여 O(n^2)으로 문제를 해결했습니다.

이 코드는 원소 하나씩 차례대로 비교하면서 값을 구하는 방법이죠.

이렇게 코드를 작성하면 아래와 같이 케이스를 해결할 수 있습니다.

테스트 케이스 결과

 

5. 마치며

오늘은 level 2의 문제를 보았지만 그 중에서도 가장 짧고 간단한 코드들을 보았습니다. level 2라고해서 level 1과 크게 다를 것은 없습니다. 다만 코드의 길이가 조금 늘어나고 level 2부터는 시간 복잡도를 생각하면서 문제를 해결하기 시작해야합니다. 이번 문제에서는 O(n^2)로도 문제가 해결됐지만 일반적으로 대기업 코딩테스트에서는 조금 힘들 가능성이 높겠죠. 문제를 해결했다고 해서 만족하고 넘어가는 것이 아니라, 조금 더 나은 코드들을 생각해보시고 그 방법으로 다시 문제를 풀어보시는 것이 좋겠습니다.

1. 문제 파악하기

- new_id는 길이가 1이상 1,000 이하인 문자열이다. → 별로 길지 않은 문자열

- new_id는 알파벳 대문자, 알파벳 소문자, 숫자, 특수문자로 구성되어 있음.

- new_id에 나타날 수 있는 특수 문자는 -_.~!@#$%^&*()=+[{]}:?,<>/ 로 한정. → 이 중에서 앞에 - _ . 만 사용가능하고 나머지는 지워야함

 

2. 문제 접근하기

이번 문제는 문제에서 주어진 7가지 단계 순서대로 구현하면 쉽게 문제를 해결할 수 있습니다. 코드는 level1 문제치곤 길다고 생각드실 수도 있지만 그렇게 어려운 알고리즘을 생각할 필요는 없다고 생각이 듭니다.

 

3. 주의할 점

- 입력으로 주어지는 new_id의 길이가 1이상 1,000 이하인 문자열이라서 무한루프에 빠지지만 않는다면 실행시간이 그렇게 오래걸리지 않을 것입니다. 7가지 단계 중 특수 문자 제거, 연속된 마침표를 제거 부분에서 while문을 이용해서 코드를 작성하신 분들이라면 무한루프를 조금 주의해서 코드를 작성하셨어야 할거 같습니다.

 

- 특수 문자 제거, 마침표 제거 등 문자열을 제거하다보면 빈문자열이 만들어지는 경우가 있을텐데, 이때 인덱싱 에러 문제로 조금 조심해야할 부분인거 같습니다.

 

4. 정답 코드 및 해석

def solution(new_id):
    del_str = ["~", "!", "@", "#", "$", "%", "^", "&", "*","(", ")", "=",
               "+", "[", "{", "]", "}", ":", "?", ",", "<", ">", "/"]
    #1단계 소문자로 바꾸기
    new_id = new_id.lower()
    #2단계 특수 문자 제거
    index = 0
    while index < len(new_id) :
        if new_id[index] in del_str :
            new_id = new_id.replace(new_id[index], "")
        else :
            index += 1
    #3단계 마침표 2번이상 연속을 하나로 치환
    while ".." in new_id :
        new_id = new_id.replace("."*2, ".")
    #4단계 마침표 처음과 끝에 위치 제거
    if len(new_id) > 0 and new_id[0] == "." :
        new_id = new_id[1:]
    elif len(new_id) > 0 and new_id[-1] == "." :
        new_id = new_id[:-1]
    #5단계 빈 문자열이면 "a"를 대입
    if new_id == "" :
        new_id = "a"
    #6단계 16자리 이상이면 15까지 남기고 제거 & 끝자리 마침표 제거
    if len(new_id) > 15 :
        new_id = new_id[:15]
    if new_id[-1] == "." :
        new_id = new_id[:-1]
    #7단계 2자리 이하면 아이디의 마지막 문자 반복
    while len(new_id) <= 2 :
        new_id += new_id[-1]
    return new_id

정답 코드 및 해석입니다. 7가지 단계로 나눠서 작성하다보니 코드가 조금 길어서 단계별로 나눠서 설명을 드리겠습니다.

 

우선 del_str이라는 리스트가 있습니다. 보시면 아시겠지만 지워야할 특수 문자들이 들어있습니다.

 

1단계는 대문자로 되어 있는 문자열을 소문자로 바꾸는 과정입니다. 파이썬에서는 .lower() 함수를 이용해서 대문자를 소문자로, .upper() 함수를 이용해서 소문자를 대문자로 바꿀 수 있습니다. 파이썬의 장점중 하나인 유용한 내장함수가 많은 것을 활용하여 1단계는 1줄로 코드를 작성할 수 있습니다.

 

2단계는 특수 문자를 제거하는 것입니다. index라는 변수에 0을 초기화 시킵니다. 그런 다음 while문을 이용해서 new_id의 한문자씩 확인하면서 del_str에 들어 있는 특수 문자라면 replace("지울 문자", "지운 문자 대신 바꿀 문자") 함수를 이용해서 제거시킵니다. 다른 라이브러리를 사용하여 해결할 수 있는 방법도 있지만 import를 사용하지 않고 그냥 내장함수만을 이용해서 코드를 작성한다면 이렇게 간단한 방법이 있을거 같습니다. 어차피 new_id의 최대 길이가 1,000 밖에 안되니 큰 문제는 될거 같지 않습니다.

 

3단계는 마침표가 2번이상 연속된 부분을 하나로 치환하는 것입니다. 저는 개인적으로 이 부분을 가장 까다롭다고 생각했는데요. '어떻게하면 간단하고 짧게 해결할 수 있을까?' 생각했던거 같습니다. 제가 생각한 방법은 while문을 사용하여 new_id에 ".."이 있으면 "."으로 치환하는 방법입니다. "..."이더라도 앞의 ".."이 먼저 인식이 되서 ".."으로 바뀔 것이고 이는 한번 더 while문에 걸려서 "."으로 바뀝니다.("..." → ".." → ".") 이렇게 하면 3단계 코드도 간단하게 완료 할 수 있습니다.

 

4단계는 마침표가 처음이나 끝에 위치하면 제거하는 단계입니다. 이 부분은 인덱싱을 이용하면 쉽게 제거 할 수 있어서 다들 쉽게 하셨을거 같습니다. 저는 앞에 과정 때문에 길이가 0인 new_id일 경우 new_id[0]과 new_id[-1] 코드가 인덱싱 에러가 방생하여 앞에부분에 len(new_id) > 0이라는 조건을 넣어줬습니다.

 

5단계는 빈 문자열이면 "a"를 대입하는 단계입니다. 이 부분은 if문을 사용하시면 쉽게 하실 수 있는 부분이니 넘어가겠습니다.

 

6단계는 16자리 이상이면 15자리까지 남기고 나머지는 제거 & 끝자리에 마침표가 있으면 제거하는 단계입니다. 이부분도 if문을 사용하면 쉽게 구현할 수 있습니다. new_id가 15자리 초과라면 new_id를 슬라이싱하여 0~14까지 자릅니다. 그런 다음 new_id의 끝자리에 "."이 있으면 제거해줍니다.

 

마지막 7단계는 new_id가 2자리 이하라면 new_id의 마지막 문자를 반복하여 new_id의 길이를 3으로 만드는 단계입니다. 이 부분도 while, if문을 사용하면 쉽게 구현할 수 있었습니다.

 

이렇게 코드를 작성하면 다음과 같은 결과로 통과를 할 수 있었습니다.

 

테스트 케이스 결과

5. 마치며

이번 문제도 크게 어려운 부분은 없었습니다. 빈문자열 처리, 무한루프에 빠지지만 않으셨다면 금방 해결하셨을거 같습니다. 저는 replace(), lower() 등을 사용하여 쉽게 구현했는데 내장 함수를 잘 모르시면 힘드셨을거 같습니다. 파이썬을 사용하신다면 파이썬의 큰 장점 중 하나인 내장 함수를 익히셔서 유용하게 사용하는 것을 추천드립니다. 내장 함수가 많은 만큼 기능은 비슷하지만 시간 복잡도, 공간 복잡도가 차이가 나는 함수들이 있습니다. 이런 것들을 잘 알고 비교해서 사용하신다면 시간초과가 나는 문제를 잘 해결하실 수도 있습니다.

1. 배경

 블로그 글을 쓴지 3일정도가 되었고 Algorithm 카테고리에는 이제 2개의 글이 올라갔다. Algorithm이라고 거창하게 카테고리를 만들었지만 실제로는 그냥 내가 문제를 푼 해석을 풀이해논 페이지이다. 다양한 언어를 능숙하게 사용한다면 좋겠지만 이전 글을 쓰면서 '내가 다른 언어를 사용할 일이 있을까?'라는 생각이 들었다. 그 생각 결과 음.. 굳이? 라는 생각이 들었다. 그래서 나는 어쩌다가 Python을 선택하게 되었을까를 생각하다가 이렇게 글을 써본다.

 

2. Python이란?

 Python은 1991년 프로그래머인 귀도 반 로섬이 발표한 고급 프로그래밍 언어이다. Python의 특징이라고 하면 인터프리터, 객체지향적 언어라는 것이다. 객체지향적이라는 것은 다른 언어에서도 많이 들었을테니 대충 알 것이고 인터프리터 언어라는 것은 특별한 컴파일이 없어도 바로 idle에서 실행할 수 있는 것을 말한다. Java를 사용해본 유저라면 코드를 작성하고 컴파일을 거친다음 작성한 코드가 실행된다. 하지만 Python에서는 해당 코드를 작성하고 실행을 누르면 바로 실행 시킬 수 있다. 또한 terminal에서도 python3(또는 python2)를 입력하면 바로 실행되어 코드를 작성해도 바로바로 결과를 얻을 수 있다.

 Python은 처음 코딩을 시작하는 사람들에게 가장 많이 추천되는 언어이다. 그 이유는 다른 언어에 비해서 엄청 쉽다. 처음 코딩을 시작해서 Python을 만져보는 사람들은 "이것도 어려워 죽겠는데 도데체 뭐가 쉽다는거지?" 라는 생각이 들 수도 있지만 C++과 Java 등을 만지다가 Python을 만져본 사람들은 엄청난 신세계의 경험을 했을 것이다.

 이러한 이유로 Python의 인기는 계속해서 증가하고 있으며 2016년 알파고와 이세돌의 대국을 통해서 엄청난 관심을 받게된 인공지능을 개발하는 라이브러리인  Pytorch와 TensorFlow도 Python을 기반해서 나왔다.

 

3. 나는 어쩌다가 Python을?

 그렇다면 나는 왜 Python을 선택했을까? 처음부터 나도 Python을 주언어로 선택한 것은 아니다. 대학생 1학년 2학기에 처음으로 자바를 다뤄봤다. 그 당시에는 무슨 생각이었는지 모르겠지만 자바를 만지면서 "와 재미있다.", "멋진 언어다." 라는 생각을 했었다. 지금 생각하면 왜 그랬는지 모르겠다. System.out.println()과 같이 긴 코드들이 멋져보인건지, 어디에 홀린건지..ㅋ 그러다가 방학을 보내고 다시 개강을 했을 때 자바를 보니 이게 무슨일? 하나도 안떠올라.. 간단한 코드를 작성하는데도 빨간 글씨 에러가 얼마나 많이 나오던지..ㅋㅋㅋ (이때부터라도 정신차리고 블로그에 기록을 했어야했네;;) 이전 학기에는 멋져보이던 언어가 너무 귀찮게 느껴졌다. class선언하는데 private, public 고려해야하고, '왜이리 코드들이 길어..' 라는 생각이 계속 들었다. 그래서 점점 java와 거리가 멀어졌고, 2학년 1학기에는 C++수업을 들었다. C++은 처음부터 나랑 안맞는다는 느낌이 들었다 ㅋㅋㅋㅋㅋ iterator도 복잡했고 뭐하나 쉬운게 없었다.. 그래도 소프트웨어과인데 내 주언어는 하나 있어야하지 않을까하다가 Python을 선택했다. 위에서 설명했던 쉽다는 장점이 크게 느껴졌고, 코드를 작성하기 편했다. 그래서 '그래 이게 나한테 맞는거 같아.' 라고 생각이 들었고 그때부터 주 언어로 선택한걸로 기억한다.

 

4. Python을 사용하면서 불편한 점은?

 Python의 큰 장점이 쉽고 편리한 내장함수와 라이브러리가 많다는 장점이 너무 커서 크게 불편했던 적은 없다. 하지만 몇가지 떠오르긴한다.

 우선 다른 언어에 비해 속도가 느린편인 것입니다. 이게 평소에는 별로 차이를 못느끼지만 코딩테스트에서 똑같은 알고리즘은데 C++로는 통과하고 Python으로는 통과하지 못하는 경우가 종종 발생합니다. 이러한 차이가 있는 이유는 파이선이 동적 타입이기 때문에 변수 이름 앞에 string이나 int와 같은 타입 지정없이 알아서 해줍니다. 우리에겐 정말 편리한 기능이지만 그 과정이 파이썬에게는 많이 부담이 되는 과정입니다. 이와 반대로 C++과 Java와 같은 언어들은 변수 앞에 int, string 등등 타입을 지정해줍니다.

 두번째로는 코딩테스트에서 Python을 지원하지 않는 회사가 종종 있습니다. 요즘은 대세 언어이고 인공지능에도 많이 사용하다보니 거의 없지만 대표적인 대기업으로는 삼성이 있습니다. 삼성도 테스트나 때에 따라서 다른거 같지만 삼성 SW역량 테스트에서 Java, C++, C만 지원하는 경우가 있습니다. 정확한 테스트 이름은 기억이 안나지만 올해 삼성에서 열린 코딩테스트가 있었는데 한번 테스트삼아 지원을 하려고했지만 Python이 없는거 보고 좌절... OTL. 다른 회사들도 종종 Python을 테스트 자체에서 지원하지 않는 경우가 종종 있다고 들었습니다. 그 이유는 뭐 여러가지가 있겠지만 인기있는 언어를 이렇게 제외시킨다는 것은 주언어 사용자 입장에서는 많이 슬픕니다..

 

5. Python 관련 추천 책

 저는 Python에 대한 기본 지식은 google에서 검색하면서 공부했고 알고리즘 관련 공부는 아래 책을 사용했습니다.

파이썬 알고리즘 인터뷰

저도 대학 동기한테 추천받아서 읽어보았는데 내용이 튼튼하고 잘 설명되어있었습니다. 개인적으로 책 읽는 것을 별로 안좋아하는데도 나름 재미있게 읽으면서 공부했습니다. 초보자분들도 쉽게 이해할 수 있고 여러 알고리즘에 대해서 익힐 수 있는 책이었습니다.

 코딩테스트 연습은 저는 프로그래머스를 주로 사용하는 편입니다. 카카오 관련 문제나 여러 코딩테스트 문제들이 많이 올라오거든요. 다른 사람들은 백준 사이트도 많이 이용하시는 것 같습니다. 여러 사이트를 이용해보시고 자신에게 맞는 사이트에서 공부하시는게 조금이라도 더 즐겁고 편하겠죠?

 

6. 마치며

이번 글에서는 Python에 대해서 간단하게 소개하며 제가 왜 Python을 사용하게 되었는지 소개했습니다. 혹시 아직 주언어를 정하지 못하신 분들, 처음 코딩을 시도하시는 분들이라면 Python을 주언어로 해보시는 것을 추천드립니다. 정말 편리하거든요bb

읽어주셔서 감사합니다.

1. 문제 파악하기

- 0이상 100이하인 정수가 들어있는 numbers의 길이는 2이상 100이하이다.

→ 그렇게 길지 않은 데이터네요.

- 더할 수 모든 수를 리스트에 담아서 오름차순으로 return 해야한다.

 

2. 문제 접근하기

 이 문제를 읽고 서로 다른 인덱스에 있는 두 개의 수를 더하라는 말을 보고 바로 이중 for문이 떠오릅니다. 이중 for문을 사용하면 시간복잡도는 n^2이라 좋은 성능의 알고리즘은 아니지만 문제 조건에서 numbers의 길이가 2이상 100이하라고 주어진 것을 보니 시간제한이 걸릴만한 길이는 아닐거 같습니다.

 

3. 주의할 점

얼핏보면 간단한 알고리즘인거 같지만 정답을 맞추기 위해서 몇가지 주의를 놓치면 조금 헤맬수 있습니다.

 

- return할 리스트에 중복 값이 들어가면 안된다.

→ 입출력의 예를 보면 2+3, 1+4로 5가 여러번 나올 수 있습니다. 하지만 result에 보면 5가 한번밖에 나타나 있지 않죠. 중복 값이 나타난다면 제거를 해야한다는 뜻입니다.

 

- return하는 리스트는 오름차순으로 정렬해야한다.

→ 쉬운 조건이지만 놓칠 수 있는 조건입니다. 문제를 꼼꼼히 읽어야할 필요가 있습니다.

 

Python을 능숙하게 사용하신 분들이라면 해당 문제를 쉽고 빠르게 해결하셨을 문제입니다. 하지만 Python을 다룬지 얼마 안되신 분들이나, Python 내장함수를 잘 모르시는 분들이라면 코드가 길어질 수 있습니다. Python의 장점은 이전 글에도 설명을 드렸지만 다른 언어보다 내장함수가 잘 되어있어 Python 내장함수만 알아도 문제를 쉽고 짧은 코드로 해결할 수 있습니다.

 

4. 정답 코드 및 해석

따라서 정답 코드는 아래와 같다고 할 수 있습니다.

 

정답 코드
정확성 테스트 결과

 앞에서 설명했다 싶이 2중 for문을 사용했습니다. 앞에 for문은 numbers의 처음부터 시작해야하며 numbers의 끝까지 가면 됩니다. 두번째 for문은 i랑 다른 인덱스에서 시작해야하므로 i+1에서 시작하면 됩니다. 그래서 answer 리스트에 두 인덱스에 해당하는 값을 더합니다.

 이렇게 for문을 빠져나오면 answer에는 아까 주의할 점에서 언급한 것과 같이 중복 된 값이 들어있고 정렬되어 있지 않은 상태입니다. Python에는 아주 감사하게도 set()이라는 함수와 list()라는 함수가 있습니다. set()은 우리가 흔히아는 집합입니다. set이 list와 다른 점은 중복된 값을 저장할 수 없으며 순서를 보장하지않는다는 점입니다. 따라서 우리는 중복 값을 제거하기 위해서 set(answer)를 사용합니다. 하지만 이렇게만 하면 정렬할 때 조금 귀찮아집니다. 따라서 중복을 없앤 set(answer)에 list(set(answer))를 사용하여 다시 리스트로 만들어줍니다. 리스트에는 sort()함수를 사용하여 쉽게 오름차순으로 만들 수 있습니다.

 이렇게 해서 return을 하면 정답을 얻어 낼 수 있습니다.

 

5. 다른 풀이방법

 이렇게 2중 for문을 사용하지 않고 이 문제를 해결하는 방법은 없을까요? 있습니다. itertools.combinations를 사용해서 문제를 해결할 수 있습니다. itertools.combination은 특정 리스트 원소로 조합을 만들어 냅니다. itertools 라이브러리도 사용법을 안다면 많은 알고리즘 문제를 깔끔하게 해결할 수 있는 경우가 많습니다. 단점은 라이브 코딩도중 해당 라이브러리 이름이 떠오르지않는다면 난감하다는 점이겠죠..;;; 또 저같은 경우는 itertools를 거의 사용하지 않는 편인데요. 그 이유는 아래에서 다시 언급하겠습니다. itertools 라이브러리에는 combinations뿐만 아니라 다른 유용한 함수들도 많습니다. combinations와 다른 함수들을 사용하는 방법은 google에 직접 찾아보시길 바랍니다.

 itertools.combinations를 사용하면 아래와 같이 코드를 작성할 수 있으며 해당 코드 결과입니다.

combinations를 활용한 정답 코드
combinations를 활용한 정확성 테스트 결과

보시면 2중 for문을 사용하진 않았지만 테스트에 소요되는 시간은 더 오래 걸린 것을 확인할 수 있습니다. 왜 이런 현상이 발생하는지는 combinations 함수를 실행할 때 어떤 형식으로 조합을 만들어 내는지를 분석해야겠지만 제 예상으로는 애초에 조합을 생성할 때도 2중 for문을 사용하면서 조합을 만들어 내지않나 싶습니다. 따라서 뭔가 2중 for문을 사용하지 않아서 조금 편안해진 기분은 들지만 걸린 시간을 보니 찜찜한건 남아 있습니다... 저는 오히려 itertools를 사용할 때 이렇게 시간이 더 오래 걸리는 경우가 많아서 해당 라이브러리를 잘 사용하지 않습니다. 실제로 좀 더 난이도 있는 문제에서 사용하다간 시간초과에 걸리는 경우도 종종 있었습니다. 따라서 상황에 맞게 잘 사용하는 방법 밖에 없어보입니다.

 

6. 마치며

이번 문제는 크게 어려운 문제는 아니었습니다. 프로그래밍을 처음하시는 분들도 다른 문제들이랑 비교했을 때 비교적 쉽다고 생각하셨을 문제입니다. 하지만 Python의 내장함수에 대해서 지식이 없으셨다면 코드가 길어지거나 헷갈리셨을 수도 있었을거 같습니다. Python을 주언어로 사용하실 목적이라면 Python의 내장함수와 주요 라이브러리를 잘 숙지해두시는 것이 중요하겠습니다.

* 해당 문제풀이는 Python을 이용했습니다.

1. 문제 파악하기

- 마라톤에 참가한 선수들이 있는데 그 수는 1명 이상, 100,000명 이하이다.

- completion의 길이는 participant의 길이보다 1 작다. → 완주하지 못한 사람은 1명이다.

- 참가자 이름은 알파벳 소문자로 이루어져 있으며 동명 이인이 있을 수 있다.

 

2. 문제 접근하기

우선 참가자 이름들이 들어 있는 participant와 완주자 이름들이 들어있는 completion은 리스트로 들어 있다. 가장 쉽게 해결하는 방법은 participant와 completion을 먼저 정렬한 다음 for문을 돌면서 같은 index 에 같은 원소가 아닌 참가자 이름을 찾아내면 된다. 파이썬은 리스트에 대한 다양한 함수들이 있으며 이 함수들을 이용하면 다른 언어보다 쉽고 빠르게 코드를 작성할 수 있다는 장점이 있다. 파이썬을 사용한다면 이 장점을 잘 사용하는 것이 중요하다.

 

3. 주의할 점

- for문을 돌릴 때 participant를 기준으로 돌리면 어떻게 될까

→ 문제 파악하기의 2번째에서 확인 할 수 있듯이, completion의 길이가 participant의 길이보다 1 작기 때문에, participant를 기준으로 돌리게 된다면 participant의 마지막 원소까지 갔을 때, index에러가 발생할 수 있다. 따라서 for문은 completion을 기준으로 해야한다.

 

- completion의 원소를 다 찾았는데도 발견을 못한 경우는?

→ for문을 통해서 completion의 원소를 다 확인했는데도 정답을 찾지 못했다는 뜻은 무엇일까? 바로 participant의 마지막 원소가 정답이라는 뜻이다. 따라서 for문을 통과한 뒤에 answer가 빈 문자열인지 확인하는 if문이 필요하다.

 

4. 정답 코드

따라서 정답 코드는 아래와 같다고 할 수 있다.

participant와 completion을 정렬한 뒤, completion을 기준으로 for문을 돌린다. 이때 participant의 i번째와 completion의 i번째 가 다르다는 것은 i번째 participant가 completion에 없다는 뜻이므로 이 사람이 완주를 하지 못한 사람이다. 정답을 찾은 뒤에는 뒤에 원소를 굳이 볼 필요가 없으므로 break문을 통해서 빠져나온다. 이렇게 찾았다면 다행이지만 completion의 마지막 원소까지 보았을 때 answer를 찾지 못한 경우(paritipant의 마지막 원소가 정답일 때)가 있으므로 if문을 통해서 확인을 해준다.

정답 코드

 

5. 문제점

이 코드는 정답처리가 되긴한다. 정답처리가 되지만 과연 이 코드에 문제가 없다고 할 수 있을까? 이런 단순한 코드에는 여러 문제가 있을 수 있지만 가장 먼저 떠오르는 것은 호율성 문제이다. 이번 문제는 level 1이기 때문에 프로그래머스에서 호율성 테스트 통과 기준을 엄청 쉽게 해놨을 것이다. 만약 실제 코딩테스트에서 이런식으로 간단하게 코드를 작성했을 경우, 시간초과가 나타날 가능성이 높다.

실제 이번 문제에서 제출 버튼을 눌렀을 때 실행시간을 보면, 만족스럽지 못할 것이다.

정확성 테스트 결과
효율성 테스트 결과

 

코드가 간단해서 나쁠건 없다. 하지만 코드가 간단하면 실행시간이 오래걸리는 경우가 많고 너무 단순하게(1줄 코드)로 작성하면 처음 보는 사람은 '이 코드를 왜 이렇게 작성했을까?' 라는 생각이 들 수 도 있다. 이 trade off관계를 잘 조절하는 것이 중요할거 같다.

 

그렇다면 이 시간 문제를 어떻게하면 조금 더 줄일 수 있을까?

그 힌트는 문제 옆에있는 해시에 있다.

프로그래머스는 문제 옆에 어떤 알고리즘을 사용하면 좋은지 나와있다.

 

해시에 대해서 간단하게 설명하자면 키값마다 다른 값이 만들어진다. 파이썬에는 hash 함수가 기본으로 사용할 수 있는데 hash("a")와 hash("b")는 다른 값이 만들어진다.

따라서 hash 함수를 이용해서 코드를 작성하면 다음과 같이 적을 수 있다.

hash 함수를 이용한 정답

 

해당 코드로 제출 버튼을 누르면 다음과 같은 시간이 걸린다.

정확성 테스트 결과
효율설 테스트 결과

 

정확성 테스트에선 그렇게 큰 차이가 없지만 효율성 테스트에선 꽤 많은 차이가 나는 것을 확인할 수 있다.

 

6. 마치며

알고리즘 문제를 풀고나면 정답을 맞춘 것도 좋지만 다른 사람은 어떻게 생각을 했고 어떤 식으로 작성했는지 확인하는 것도 도움이 많이 된다. 가끔씩은 나도 모르던 함수를 이용해서 엄청 쉽게 푼 사람들도 있으며 어떤 사람은 문제 해결 시작을 나와 다른 방향으로 진행해서 해결한 사람들도 있다.

+ Recent posts