Python/프로그래머스

[프로그래머스 lv.1]성격 유형 검사하기(건너뛴 문제 다시풀기)

묘걍 2023. 9. 25. 19:21

문제

나만의 카카오 성격 유형 검사지를 만들려고 합니다.
성격 유형 검사는 다음과 같은 4개 지표로 성격 유형을 구분합니다. 성격은 각 지표에서 두 유형 중 하나로 결정됩니다.

4개의 지표가 있으므로 성격 유형은 총 16(=2 x 2 x 2 x 2)가지가 나올 수 있습니다. 예를 들어, "RFMN"이나 "TCMA"와 같은 성격 유형이 있습니다.

검사지에는 총 n개의 질문이 있고, 각 질문에는 아래와 같은 7개의 선택지가 있습니다.

  • 매우 비동의
  • 비동의
  • 약간 비동의
  • 모르겠음
  • 약간 동의
  • 동의
  • 매우 동의

각 질문은 1가지 지표로 성격 유형 점수를 판단합니다.

예를 들어, 어떤 한 질문에서 4번 지표로 아래 표처럼 점수를 매길 수 있습니다.

이때 검사자가 질문에서 약간 동의 선택지를 선택할 경우 어피치형(A) 성격 유형 1점을 받게 됩니다. 만약 검사자가 매우 비동의 선택지를 선택할 경우 네오형(N) 성격 유형 3점을 받게 됩니다.

위 예시처럼 네오형이 비동의, 어피치형이 동의인 경우만 주어지지 않고, 질문에 따라 네오형이 동의, 어피치형이 비동의인 경우도 주어질 수 있습니다.
하지만 각 선택지는 고정적인 크기의 점수를 가지고 있습니다.

  • 매우 동의나 매우 비동의 선택지를 선택하면 3점을 얻습니다.
  • 동의나 비동의 선택지를 선택하면 2점을 얻습니다.
  • 약간 동의나 약간 비동의 선택지를 선택하면 1점을 얻습니다.
  • 모르겠음 선택지를 선택하면 점수를 얻지 않습니다.

검사 결과는 모든 질문의 성격 유형 점수를 더하여 각 지표에서 더 높은 점수를 받은 성격 유형이 검사자의 성격 유형이라고 판단합니다. 단, 하나의 지표에서 각 성격 유형 점수가 같으면, 두 성격 유형 중 사전 순으로 빠른 성격 유형을 검사자의 성격 유형이라고 판단합니다.

질문마다 판단하는 지표를 담은 1차원 문자열 배열 survey와 검사자가 각 질문마다 선택한 선택지를 담은 1차원 정수 배열 choices가 매개변수로 주어집니다. 이때, 검사자의 성격 유형 검사 결과를 지표 번호 순서대로 return 하도록 solution 함수를 완성해주세요.


제한사항

  • 1 ≤ survey의 길이 ( = n) ≤ 1,000
    • survey의 원소는 "RT", "TR", "FC", "CF", "MJ", "JM", "AN", "NA" 중 하나입니다.
    • survey[i]의 첫 번째 캐릭터는 i+1번 질문의 비동의 관련 선택지를 선택하면 받는 성격 유형을 의미합니다.
    • survey[i]의 두 번째 캐릭터는 i+1번 질문의 동의 관련 선택지를 선택하면 받는 성격 유형을 의미합니다.
  • choices의 길이 = survey의 길이
    • choices[i]는 검사자가 선택한 i+1번째 질문의 선택지를 의미합니다.
    • 1 ≤ choices의 원소 ≤ 7

 

 

예전 풀이

생각

 

코드

 

다시 생각해보기

생각

문제 이해가 잘 안된다.

일단 내가 이해한 바로는 survey의 원소 순서대로 매우 비동의 ~ 매우동의에 대한 점수를 얻는다.

예를 들어 'RT'이면 1~3번 선택시 라이언형이 점수를 얻고 5~7번 선택시 튜브형이 점수를 얻지만

'TR'이면 1~3번 선택시 튜브형이 점수를 얻고 5~7번 선택시 라이언형이 점수를 얻는다.

딕셔너리를 만들어

key에는 R,T,C,F,J,M,A,N을 넣고 value는 비워둔다.

survey를 for문 돌고

choices를 for문 돌면서

만약 choices[j]가 4보다 작으면

survey[i][0]와 같은 key의 value에 += (4-choices[j])

만약 choices[j]가 4보다 크면

survey[i][1]와 같은 key의 value에 += (choices[j] - 4)

 

딕셔너리 key들을 for문 돌면서

홀수번째 key에만 접근하여 (이건 if문을 사용할지 while문을 써서 i += 2를 사용할지..)

해당 key i와 i+1을 비교하여 value의 값이 더 큰 key를 answer에 += 해준다.

 

코드

1차 시도

def solution(survey, choices):
    answer = ''
    type_list = ['R', 'T', 'C', 'F', 'J', 'M', 'A', 'N']
    type_dict = {key: 0 for key in type_list}
    
    for i in range(len(survey)):
        for j in range(len(choices)):
            if choices[j] < 4:
                if survey[i][0] in type_dict:
                    type_dict[survey[i][0]] += (4 - choices[j])
            elif choices[j] > 4:
                if survey[i][1] in type_dict:
                    type_dict[survey[i][1]] += (choices[j] - 4)
            else:
                continue
    
    # type_dict_keys = list(type_dict.keys())
    for idx, key in enumerate(type_list):
        if idx % 2 == 0:
            cur_val = type_dict[key]
            next_val = type_dict[type_list[idx+1]]
            if cur_val > next_val:
                answer += key
            elif cur_val < next_val:
                answer += type_list[idx+1]
            else: 
                answer += key
                    
    return answer

딕셔너리 컴프리헨션에서 key: 0 for i in type_list라고 해서 난 에러. i가 아니라 key라고 해야함

** 딕셔너리 컴프리헨션 **
- 기본 구조: {key_expr: value_expr for item in iterable}
- 조건 이용하기: {key_expr: value_expr for item in iterable if condition}

- 홀수번째 key에만 접근하려면 key들을 리스트로 만들고 enumerate를 통해 리스트의 인덱스에 맞게 접근해야한다.

** enumerate() **
- 순회 가능한 객체(예: 리스트, 튜플, 문자열 등)를 인자로 받아 인덱스와 해당 항목의 값을 동시에 반환
- for 루프를 사용할 때 항목의 인덱스가 필요할 때 유용
- 기본 형태: enumerate(iterable, start=0)
     - iterable: 순회 가능한 객체
     - start: 인덱싱을 시작할 값. 기본 값은 0

- 점수를 부여한 뒤 다음 i로 넘어가도록 해야한다. 그런 코드를 안 넣었더니 i하나에 대해서 choices의 모든 요소를 다 돌고 있다. 아니면 이중 for문으로 하지말고 for문을 하나로 해야겠다. 어차피 요소로 접근하는 게 아니라 인덱스로 접근하는거니까

 

2차 시도

def solution(survey, choices):
    answer = ''
    type_list = ['R', 'T', 'C', 'F', 'J', 'M', 'A', 'N']
    type_dict = {key: 0 for key in type_list}
    
    for i in range(len(survey)):
            if choices[i] < 4:
                if survey[i][0] in type_dict:
                    type_dict[survey[i][0]] += (4 - choices[i])
            elif choices[i] > 4:
                if survey[i][1] in type_dict:
                    type_dict[survey[i][1]] += (choices[i] - 4)
            else:
                continue
    
    # type_dict_keys = list(type_dict.keys())
    for idx, key in enumerate(type_list):
        if idx % 2 == 0:
            cur_val = type_dict[key]
            next_val = type_dict[type_list[idx+1]]
            if cur_val > next_val:
                answer += key
            elif cur_val < next_val:
                answer += type_list[idx+1]
            else: 
                answer += key
                    
    return answer

- 성공!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

다른 사람 풀이

def solution(survey, choices):
    answer = ''
    dic= {"R" : 0,"T" : 0,"C" : 0,"F" : 0,"J" : 0,"M" : 0,"A" : 0,"N" : 0 }
    
    for s,c in zip(survey,choices):
        if c>4:     dic[s[1]] += c-4
        elif c<4:   dic[s[0]] += 4-c
    
    li = list(dic.items())
    
    for i in range(0,8,2):
        if li[i][1] < li[i+1][1]: answer += li[i+1][0]
        else:   answer += li[i][0]
        
    return answer

코드 스스로 해석하고 이해하기

- 내 코드 상 type_list를 다시 사용하지 않으니 어쩌면 이렇게 딕셔너리를 설정하는 게 더 효율적일 수도 있겠다

- zip() 함수를 통해 survey와 choices를 동시에 순회하며 각 항목을 s와 c에 할당함

** zip() **
- 여러 개의 순회 가능한 객체 (예: 리스트, 튜플, 문자열 등)를 인자로 받아 각 객체의 항목들을 순서대로 튜플로 묶은 이터레이터를 반환
- 기본 사용법: zip(iterable1, iterable2, ...)
- 가장 짧은 입력 이터러블의 길이를 기준으로 항목을 묶는다. 만약 다양한 길이의 이터러블이 주어지면, 가장 긴 이터러블의 나머지 항목은 무시된다.
- 두 개 이상의 이터러블도 처리할 수 있다
- 이터레이터이므로 한 번 순회하면 그 내용을 잃게 된다. 필요하다면 list() 또는 다른 순회 가능한 형태로 변환하여 저장해야 함
- zip()의 역 연산: zip()을 통해 묶은 항목들을 다시 분리하려면 zip(*zipped_data) 형태로 사용할 수 있다.

- 만약 choices의 한 요소가 4보다 크다면 survey 해당 요소에서 4를 빼고

- 4보다 작다면 4에서 survey 해당 요소를 빼준다

(뭔가 이 부분 내가 혼자 생각해낸 것과 같다는게 뿌듯)

- 생성된 딕셔너리의 key와 value를 튜플형태로 받아와 리스트를 만든다

- 0부터 8까지 2단계씩 건너뛰면서 리스트를 순회한다

- 만약 리스트의 특정 요소 튜플의 1번인덱스(딕셔너리 value값 = 문항에 더해진 점수)이 다음 요소 튜플의 1번 인덱스보다 작다면 다음 요소 튜플의 0번(딕셔너리 key값 = 유형)을 붙여주고

- 그 외 모든 경우에 대해서는 현재 요소 튜플의 0번을 붙여준다.

 

 

두 코드다 시간 복잡도는 O(n)이라서 큰 차이가 없다고 한다.

하지만 다른 사람의 코드가 더 간결하니

나도 앞으로 더 간결하게 만들 수 없는지에 대해 고민해봐야겠다.