카테고리 없음

[프로그래머스 lv.1] 신고 결과 받기 (건너뛴 문제 다시풀기)

묘걍 2023. 9. 21. 15:56

문제

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

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

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

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

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

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

이용자의 ID가 담긴 문자열 배열 id_list, 각 이용자가 신고한 이용자의 ID 정보가 담긴 문자열 배열 report, 정지 기준이 되는 신고 횟수 k가 매개변수로 주어질 때, 각 유저별로 처리 결과 메일을 받은 횟수를 배열에 담아 return 하도록 solution 함수를 완성해주세요.


제한사항

  • 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 순서대로 각 유저가 받은 결과 메일 수를 담으면 됩니다.

 

예전 풀이

생각

문제를 정확히 이해를 못함,,

유저 리스트가 있고,

신고'한'유저 - 신고 '당한'유저 리스트가 있고

k는 정지 기준인데

메일 받은 횟수를 구하라...

 

메일은 신고 '당한'사람이 k번 이상 신고'당했'을 경우 신고'한'사람들한테 보내는건가?

아니면 신고 '한'사람이 신고 할 때 마다 신고'당한'사람이 신고되었다고 메일을 보내는건가?

후자의 경우 k도 필요없고 딱히 의미 없는 메일 같은데,,

메일 결과 중에 "apeach"가 "muzi"를 신고했습니다.가 있는 것 보면 k번 신고당하지 않은 신고 '당한'사람에 대해서도 메일을 받는 것 같은데...

 

코드

 

 

다시 생각해보기

생각

딕셔너리를 만들어야할 것 같다.

key: 신고'한' 유저, value: 신고'당한' 유저

딕셔너리의 중복을 없애면 key value전체가 같은 경우만 지워지나..?

그 후에 value의 각 값을 count해야되는데 여기는 어떻게 구현할지 모르겠다.

해당 value의 count가 k이상이면

해당 value를 가지고 있는 key 각각에 count +1을 해준다 

 

딕셔너리는 report의 for문을 돌면서

report[i][0]가 key, report[i][1]이 value

 

value의 각 값을 count하는 건 또 다른 딕셔너리를 만들까?

key는 신고 당한 유저, value는 신고된 횟수

일단 declaration을 다 돌면서 신고 당한 유저가 몇 번 나오는지 count하고 최종 결과를 count_dict에 추가하는 식?

 

만약 count_dict의 특정 value 값이 k 이상이라면 해당 key를 value로 가진 declaration의 key값을 가져와 리스트로

 

또 딕셔너리를 만들어서

declaration의 key를 그대로 key로 사용하고

declaration에서 suspend[i]를 value로 가지고 있는 key의 value에 +1을 해준다

코드

1차 시도

from collections import Counter
def solution(id_list, report, k):
    answer = []
    # 신고 현황 딕셔너리 만들기
    declaration ={}
    for i in range(len(report)):
        key, value = report[i].split()
        declaration[key] = value
    # 신고된 유저 count
    value_counts = Counter(declaration.values())
    count_dict = dict(value_counts)
    # 신고 당한 횟수가 k 이상인 유저 (정지 당할 유저)
    suspend = [key for key, value in count_dict.items() if value >= k]
    # 이메일을 받을 유저 - 몇 개의 이메일을 받을지
    result = dict.fromkeys(declaration,0)
    for i in suspend:
        if i in result:
            result[i] += 1
    answer = list(result.values())
    return answer

- 딕셔너리는 애초에 생성할 때 중복을 허용하지 않기 때문에 따로 중복 제거를 하지 않아도 된다.

- report[i][0]는 해당 문자열의 첫 번째 글자를 가져온다. 그리고 report[i][1]은 두 번째 문자를 가져온다. 그래서 이렇게 나누려고 하면 안되고 split()을 활용해야 했다.

- 딕셔너리의 value들의 갯수를 체크하기 위해 collections 모듈의 counter클래스를 활용해야했다

** from collections import Counter **
- collections: 파이썬의 내장 라이브러리
- Counter: 요소의 개수(빈도)를 세는 데 사용되는 딕셔너리 서브 클래스
- 개수 세기: 주어진 자료구조 내의 요소의 빈도를 세어 딕셔너리 형태로 반환. 키는 요소, 값은 해당 요소의 빈도수
- 가장 흔한 요소 반환: most_common([n])메소드를 사용하여 가장 흔한 요소 n개를 반환
- 기타 연산: Counter 객체는 딕셔너리와 유사하게 동작하지만, 두 Counter 객체 간 덧셈, 뺄셈, 교집합, 합집합 연산 지원
** dictionary.items() **
- 딕셔너리명.items()는 해당 딕셔너리의 key-value 쌍들을 튜플 형태로 반환한다
** 리스트 컴프리헨션 (List Comprehension) **
- 파이썬에서 리스트를 생성하는 간결하고 표현력 있는 방법
- 기본적으로는 for-loop를 사용한 리스트 생성 방법을 더 간결하게 표현하는 문법
- 기본 구조: [expression for item in iterable if condition]
     - expression: 리스트의 각 항목을 표현하는 부분 (리스트에 들어가게 될 것)
     - item: 순회하는 각 항목을 대표하는 변수 (for문에서 내가 보통 쓰는 i 같은 것)
     - iterable: 순회 가능한 객체 (예: 리스트, 딕셔너리, 문자열 등)
     - condition: 조건문. 이 조건에 맞는 항목만 리스트에 포함
** fromkeys **
- 딕셔너리 클래스인 dict의 클래스 메서드
- 주어진 키의 목록 (또는 다른 순회 가능한 객체)으로 새로운 딕셔너리를 생성한다.
- 각 키에 대한 기본 값을 설정할 수도 있다.
- 기본 사용법: dict.fromkeys(sequence, value)
     - sequence: 순회 가능한 객체, 새로운 딕셔너리의 key로 사용된다.
     - vlaue(선택사항): 모든 키에 대한 기본값. 지정하지 않으면 None

딕셔너리 중복 허용하지 않는 것이 key, value 모두 같을 때만 허용하지 않는거 아니었나? 내가 코드를 잘못 짠건지 muzi가 신고한 frodo와 neo가 따로 저장되지 않고 한 key에 덮어쓰기 되어버린다

다른 사람 풀이

def solution(id_list, reports, k):
    stop = []
    answer = [0] * len(id_list)
    dicReports = {id: [] for id in id_list}
    for i in set(reports):
        report = i.split(' ')
        stop.append(report[1])
        dicReports[report[0]].append(report[1])
        
    stop = set([i for i in stop if stop.count(i) >= k])

    for key, value in dicReports.items():
        for s in stop:
            if s in value:
                answer[id_list.index(key)] += 1
    return answer

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

- 신고로 중지될 id 저장할 빈 리스트 선언

- answer에 유저 수 만큼 자리를 만들어 놓기, 0으로 초기화

- 유저들 목록을 돌면서 id를 key로, 빈 리스트를 value로 갖는 딕셔너리 선언

- set()을 통해 주어진 reports의 중복을 제거하고 for문을 돈다

- reports 각 요소의 공백을 기준으로 나눈 뒤 해당 결과를 report라는 새로운 리스트에 저장한다

- 위에서 선언한 stop리스트에 report의 두 번째 요소인 신고 당한 유저를 append 한다.

- dicReports에서 report[0]에 해당하는 key 값에 report[1]을 value로 넣어준다

- stop 리스트를 돌며 만약 stop내 i를 count한 값이 k이상이라면 stop에 i를 다시 append한다. 다만 중복을 제거하고

- dicReports의 key-value쌍을 순회하고

- stop내의 요소들을 순회하면서

- 만약 stop 요소가 dicReports의 vlaue내에 있다면

- id_list에서 현재 key의 인덱스를 찾아 answer리스트의 해당 위치의 값을 1 증가시킨다

 

 

def solution(id_list, report, k):
    answer = [0] * len(id_list)
    reports = { x : 0 for x in id_list}
    
    for i in set(report):
        reports[i.split()[1]] += 1
        
    for i in set(report):
        if reports[i.split()[1]] >= k:
            answer[id_list.index(i.split()[0])] += 1
    return answer

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

- 위 코드와 마찬가지로 answer에 미리 유저 수 만큼 0으로 자리를 만들어 놓는다

- reports 딕셔너리를 만들어 유저들의 ID를 key로, value는 0으로 초기화한 값을 넣는다

- 중복을 제거한 report를 순회하며

- reports의 key 중 report의 각 요소를 빈칸을 기준으로 나눈 것의 두 번째요소(= 신고 당한 유저 id)인 key의 value에 +1을 해준다

- 중복을 제거한 report를 순회하며

- 만약 reports의 key 중 report의 각 요소를 빈칸을 기준으로 나눈 것의 두 번째 요소(=신고 당한 유저 id)인 key의 value 값이 k이상인 경우

     - answer에서 id_list의 신고한 유저 인덱스와 같은 곳의 인덱스에 +1을 해준다