비둘기 둥지

[프로그래머스 / 코딩테스트 ] 2022 카카오 공채 - 신고 결과 받기 본문

Python 공부/코딩 테스트 연습

[프로그래머스 / 코딩테스트 ] 2022 카카오 공채 - 신고 결과 받기

KimDove 2022. 10. 21. 17:00
728x90

1. 문제 설명

신입사원 무지는 게시판 불량 이용자를 신고하고 처리 결과를 메일로 발송하는 시스템을 개발하려 합니다.
무지가 개발하려는 시스템은 다음과 같습니다.

 

각 유저는 한 번에 한 명의 유저를 신고할 수 있습니다.

신고 횟수에 제한은 없습니다. 서로 다른 유저를 계속해서 신고할 수 있습니다.


한 유저를 여러 번 신고할 수도 있지만, 동일한 유저에 대한 신고 횟수는 1회로 처리됩니다.
k번 이상 신고된 유저는 게시판 이용이 정지되며, 해당 유저를 신고한 모든 유저에게 정지 사실을 메일로 발송합니다.
유저가 신고한 모든 내용을 취합하여 마지막에 한꺼번에 게시판 이용 정지를 시키면서 정지 메일을 발송합니다.


다음은 전체 유저 목록이 ["muzi", "frodo", "apeach", "neo"]이고, k = 2(즉, 2번 이상 신고당하면 이용 정지)인 경우의 예시입니다.

유저 ID 유저가 신고한 ID
muzi frodo
apeach frodo
frodo neo
muzi neo
apeach muzi

각 유저별로 신고당한 횟수는 다음과 같습니다.

유저 ID 신고당한 횟수
muzi 1
frodo 2
apeach 0
neo 2

위 예시에서는 2번 이상 신고당한 "frodo"와 "neo"의 게시판 이용이 정지됩니다.
이때, 각 유저별로 신고한 아이디와 정지된 아이디를 정리하면 다음과 같습니다.

유저 ID 유저가 신고한 ID 정지된 ID
muzi ['frodo', 'neo'] ['frodo', 'neo']
frodo ['neo'] ['neo']
apeach ['muzi', 'frodo'] ['frodo']
neo 없음 없음

따라서 "muzi"는 처리 결과 메일을 2회, "frodo"와 "apeach"는 각각 처리 결과 메일을 1회 받게 됩니다.

 

이용자의 ID가 담긴 문자열 배열 id_list, 이용자가 신고한 이용자의 ID 정보가 담긴 문자열 배열 report,
정지 기준이 되는 신고 횟수 k가 매개변수로 주어질 때,

 

각 유저별로 처리 결과 메일을 받은 횟수를
배열에 담아 return 하도록 solution 함수를 완성해주세요.

 


2. 제한 사항

  • 2 ≤ id_list의 길이 ≤ 1,000
    • 1 ≤ id_list의 원소 길이 ≤ 10
    • id_list의 원소는 이용자의 id를 나타내는 문자열이며 알파벳 소문자로만 이루어져 있습니다.
    • id_list에는 같은 아이디가 중복해서 들어있지 않습니다.
  • 1 ≤ report의 길이 ≤ 200,000
    • 3 ≤ report의 원소 길이 ≤ 21
    • report의 원소는 "이용자id 신고한id"형태의 문자열입니다.
    • 예를 들어 "muzi frodo"의 경우 "muzi"가 "frodo"를 신고했다는 의미입니다.
    • id는 알파벳 소문자로만 이루어져 있습니다.
    • 이용자id와 신고한id는 공백(스페이스)하나로 구분되어 있습니다.
    • 자기 자신을 신고하는 경우는 없습니다.
  • 1 ≤ k ≤ 200, k는 자연수입니다.
  • return 하는 배열은 id_list에 담긴 id 순서대로 각 유저가 받은 결과 메일 수를 담으면 됩니다.

3. 입출력 예

id_list report k result
['muzi', 'frodo', 'apeach', 'neo'] ['muzi frodo', 'apeach frodo',
'frodo neo', 'muzi neo', 'apeach muzi']
2 [2, 1, 1, 0]
['con', 'ryan'] ['ryan con', 'ryan con', 'ryan con', 'ryan con'] 3 [0, 0]

4. 아이디어 얻어보기

4-1.  지극히 개인적인 생각인 유심히 봐야할 부분

  • 동일한 유저에게 대한 신고 결과를 1번으로만 처리한다.
    • 파이썬 내장 함수인 set을 이용하여 리스트 혹은 튜플에서 중복된 값을 하나로 합쳐준다.
    • 내장함수 set을 사용하면 list나 다른 iterator 내의 원소들 중 중복된 원소는 삭제해 준다.
zoo      = ['dove', 'pigeon', 'dove', 'dove', 'penguin', 'hippo', 'tiger', 'dog']
zoo2     = ('dove', 'dog', 'dog', 'cat', 'pigeon', 'pigeon', 'cat', 'dog', 'hippo', 'hippo')
greeting = 'Hello sir! how are you?'

print(f'before | {zoo} \nafter  | {set(zoo)}\n')
print(f'before | {zoo2} \nafter  | {set(zoo2)}\n')
print(f'before | {greeting} \nafter  | {set(greeting)}')

## 출력 결과
before | ['dove', 'pigeon', 'dove', 'dove', 'penguin', 'hippo', 'tiger', 'dog'] 
after  | {'pigeon', 'dove', 'penguin', 'dog', 'hippo', 'tiger'}

before | ('dove', 'dog', 'dog', 'cat', 'pigeon', 'pigeon', 'cat', 'dog', 'hippo', 'hippo') 
after  | {'cat', 'pigeon', 'dove', 'dog', 'hippo'}

before | Hello sir! how are you? 
after  | {'?', 'l', ' ', 'w', 'u', 'y', 'e', 's', 'a', 'o', 'h', 'H', 'i', '!', 'r'}
  • 유저 별로 신고당한 횟수와 메일 발송할 횟수를 딕셔너리에 저장하도록 하였다.
    • 특히 신고당한 횟수는 내장함수 collections의 Counter를 사용하여 구현하였다.
    • 내장패키지의 collection.Counter는 list나 다른 iterator 내의 원소들의 개수를 딕셔너리 형태로 반환해 준다.
      1. iterator의 count()메소드와 다른 점은 count() 메소드특정 원소의 개수만 정수형태로 반환해 준다는 것이다.
      2. 개인적인 생각이지만 collections.Counter가 count의 상위호환이라고 생각한다.
print(f'test 1 | {zoo} \ncount  | {zoo.count("dove")}\n')                                  
print(f'test 2 | {zoo2} \ncount  | {zoo2.count("hippo")}\n')                                  
print(f'test 3 | {greeting} \ncount  | {greeting.count("T")}\n')

## 출력 결과
test 1 | ['dove', 'pigeon', 'dove', 'dove', 'penguin', 'hippo', 'tiger', 'dog'] 
count  | 3

test 2 | ('dove', 'dog', 'dog', 'cat', 'pigeon', 'pigeon', 'cat', 'dog', 'hippo', 'hippo') 
count  | 2

test 3 | Hi! TOMATO! I'm BANANA 
count  | 2
from collections import Counter

zoo      = ['dove', 'pigeon', 'dove', 'dove', 'penguin', 'hippo', 'tiger', 'dog']
zoo2     = ('dove', 'dog', 'dog', 'cat', 'pigeon', 'pigeon', 'cat', 'dog', 'hippo', 'hippo')
greeting = 'Hi! TOMATO! I\'m BANANA'

print(f'test 1 | {zoo} \ncount  | {Counter(zoo)}\n')                                  
print(f'test 2 | {zoo2} \ncount  | {Counter(zoo2)}\n')                                  
print(f'test 3 | {greeting} \ncount  | {Counter(greeting)}\n')

## 출력 결과
test 1 | ['dove', 'pigeon', 'dove', 'dove', 'penguin', 'hippo', 'tiger', 'dog'] 
count  | Counter({'dove': 3, 'pigeon': 1, 'penguin': 1, 'hippo': 1, 'tiger': 1, 'dog': 1})

test 2 | ('dove', 'dog', 'dog', 'cat', 'pigeon', 'pigeon', 'cat', 'dog', 'hippo', 'hippo') 
count  | Counter({'dog': 3, 'cat': 2, 'pigeon': 2, 'hippo': 2, 'dove': 1})

test 3 | Hi! TOMATO! I'm BANANA 
count  | Counter({'A': 4, ' ': 3, '!': 2, 'T': 2, 'O': 2, 'N': 2, 'H': 1, 'i': 1, 'M': 1, 'I': 1, "'": 1, 'm': 1, 'B': 1})

4-2.  어떻게 구현해 볼까

  • 유저별 메일 발송 횟수가 0인 딕셔너리를 초기화 해준다.
    1. 이것도 개인적인 내용이지만 어떤 데이터를 저장할때
      이 데이터가 어떤의미인지 직관적으로 알 수 있어 딕셔너리 자료형을 참 좋아한다.
    2. 리스트에 비해 원소에 접근하는 속도가 더 빠르다고 하는데 그것 때문은 아니고 그냥 위 내용때문에 좋아한다..
mail_dict = {id : 0 for id in id_list}
  • 4-1에서 언급한 대로 내장함수 set을 이용하여 report에서 중복된 데이터를 제거해준다.
report = set(report)
  • 4-1에서 언급한 대로 collections.Counter를 이용하여 유저별 신고당한 회수를 담아준다.
## 각각의 rep은 '신고자 피신고자'로 되어 있다.
## rep.split()을 하면 ['신고자', '피신고자']로 나뉘어져 리스트에는
## ['피신고자1', '피신고자1', '피신고자2', '피신고자1', '피신고자3'] 처럼 담겨있어
## 결국 shingo_dict에는 {'피신고자1' :3, '피신고자2' : 1, '피신고자3' : 1}처럼 남게된다.
shingo_dict = Counter([rep.split()[1] for rep in report])
  • 마지막으로 신고 당한 횟수가 임계치가 넘은 유저를 신고한 유저에게 매일 발송 회수를 1회 늘려준다.
  • 그리고 유저별 메일 발송 회수가 담긴 딕셔너리의 value값 들을 이용하여 결과값을 반환해준다.
for rep in report: 
    plaintiff, defendant = rep.split()
    if shingo_dict[defendant] >= k: mail_dict[plaintiff] += 1

## 메일 발송 회수 딕셔너리의 value 값만 리스트에 담아 반환
return [cnt for cnt in mail_dict.values()]

5. 문제를 풀어봅시다.

from collections import Counter

def solution(id_list, report, k):
    
    ## 메일 보낼 회수를 저장하는 딕셔너리
    mail_dict = {id : 0 for id in id_list}
    
    ## 한 유저가 같은 유저를 여러 번 신고한 것을 1번으로 치기 위해
    ## set 자료형으로 변경해 줌.
    report = set(report)
    
    ## 신고당한 회수 집계
    shingo_dict = Counter([rep.split()[1] for rep in report])

    ## 신고한 사람에게 메일 보낼 회수 집계
    for rep in report: 
        plaintiff, defendant = rep.split()
        if shingo_dict[defendant] >= k: mail_dict[plaintiff] += 1
        
    ## 메일 발송 회수 딕셔너리의 value 값만 리스트에 담아 반환
    return [cnt for cnt in mail_dict.values()]
    
    
sol1 = solution(['muzi', 'frodo', 'apeach', 'neo'], ["muzi frodo","apeach frodo","frodo neo","muzi neo","apeach muzi"], 2)
sol2 = solution(["con", "ryan"], ["ryan con", "ryan con", "ryan con", "ryan con"], 3)

print(f'solution 1 : {sol1}')
print(f'solution 2 : {sol2}')

## 출력 결과
solution 1 : [2, 1, 1, 0]
solution 2 : [0, 0]

 

99. 참고자료

99-1. 문제 출처

  • 프로그래머스 - 2022 KAKAO BLIND RECRUITMENT 신고 결과 받기 | [문제 출처]

전체코드

 

GitHub - EvoDmiK/TIL: Today I Learn

Today I Learn. Contribute to EvoDmiK/TIL development by creating an account on GitHub.

github.com


내용 추가 이력


부탁 말씀

개인적으로 공부하는 과정에서 오류가 있을 수 있으니, 오류가 있는 부분은 댓글로 정정 부탁드립니다.

 

728x90
Comments