갱스터하우스

[Python] Lv1.신고 결과 받기 본문

코테 문제/프로그래머스

[Python] Lv1.신고 결과 받기

승갱 2022. 4. 14. 02:08

https://programmers.co.kr/learn/courses/30/lessons/92334

 

코딩테스트 연습 - 신고 결과 받기

문제 설명 신입사원 무지는 게시판 불량 이용자를 신고하고 처리 결과를 메일로 발송하는 시스템을 개발하려 합니다. 무지가 개발하려는 시스템은 다음과 같습니다. 각 유저는 한 번에 한 명의

programmers.co.kr

 

 

문제 설명

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

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

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

유저 ID 유저가 신고한 ID 설명
"muzi" "frodo" "muzi"가 "frodo"를 신고했습니다.
"apeach" "frodo" "apeach"가 "frodo"를 신고했습니다.
"frodo" "neo" "frodo"가 "neo"를 신고했습니다.
"muzi" "neo" "muzi"가 "neo"를 신고했습니다.
"apeach" "muzi" "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 ≤ 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 순서대로 각 유저가 받은 결과 메일 수를 담으면 됩니다.

 

나의 접근방법 및 풀이

처음 문제를 보고서는 뭔 말인지 알겠는데 어떻게 하지 라는 생각이 들었다.

그리고 예시에서 신고한 id설명 칸을 보고 딕셔너리를 이용해야겠다고 생각했다.

각 유저에 대한 신고 수를 받을 딕셔너리를 생성한고, 같은 유저가 다른 유저를 아무리 많이 신고해도 1회만 인정된다고 해서 중복제거가 떠올라 set()을 이용해서 중복 제거를 했다.

 

(1) 신고 수 적립하기

우선 유저들이 각각 몇 번이나 신고받았는지를 파악하기 위해 fot() 문을 이용해 report() 안의 요소를 split()해 인덱스가 1인 즉, 신고한 ID를 key값으로 신고 횟수는 value값으로 하여 위에서 설정한 딕셔너리 id_report()에 추가하였다.

 

(2) 조건 확인하기

불량 ID로 신고당했다고 모두 정지되는 것이 아닐, K번 이상일 경우에만 정지가 가능하다.

이를 판단하기 위해 신고받는 딕셔너리인 id_report value값이 k번 이상인지 확인했고, k번 미만인 경우에는 해당 key값의 value를 0으로 초기화했다.

 

(3) 신고 메일 보낼 유저 찾기

위에서 판별한 딕셔너리 id_report에서 value값이 0이 아닌 경우에만 정지되는 아이디다.

그럼 이 정지되는 아이디를 신고한 유저 ID가 누구인지, 그리고 총 신고한 것 중에서 수확이 얼마나 되는지를 찾으면 된다.

for() 문과 if() 문을 사용해서 id_report의 key값과 value값을 반복해 0이 아닌 value값이 있다면, 다시 한번 for() 문을 이용해서 report() 리스트에서 split()을 이용해 split()[1] 즉, 신고당한 ID가 해당 key값과 같은 경우 split()[0] 신고한 id를 key값으로 가지는 딕셔너리 result에서 value값을 1 증가시켜주었다.

그리고 최종적으로 우리는 신고한 수확물이 목표이기 때문에 딕셔너리 result에서 value값만을 리스트로 return 한다.

def solution(id_list, report, k):
    id_report = {i:0 for i in id_list}  ## 신고 수 받을 딕셔너리 생성
    result = {i:0 for i in id_list}  ## 신고 수 받을 딕셔너리 생성
    report = list(set(report))        ## 중복 제거하기
    #print(id_report)
    
    # 신고수 적립하기
    for user in report:
        id_report[(user.split())[1]] += 1    ## 신고자-불량이용자

    ## 조건 확인해주기
    for x, y in id_report.items():
        if k > y:       ## 조건 미달이면 해당 key의 값 0으로 만들기
            id_report[x] = 0

    ## 신고 메일 결과 도출
    for key, value in id_report.items():
        if value != 0:       ## 정지된 유저
            for i in report:
                if i.split()[1] == key:       ## 불량자가 value라면
                    result[i.split()[0]] += 1
    
    ## value값만 넣기
    return [i for i in result.values()]

print(solution(["muzi", "frodo", "apeach", "neo"], ["muzi frodo","apeach frodo","frodo neo","muzi neo","apeach muzi"], 2))

시간초과로 실패했다.

어떻게든 혼자 풀고 싶어서 이리저리 해보던 중, in 리스트를 사용해봐야겠다고 생각했다.

(2) 조건 확인하기

신고당한 횟수가 k보다 클 경우 배열에 해당 key값을 추가하였다.

 

(3) 신고 메일 보낼 유저 찾기

이중 for() 문 대신 report에 대한 for() 문을 생성하여 해당 요소를 split()한 값 중 두 번째 요소, 즉 신고당한 id가 (2) 번에서 만든 리스트 r에 존재한다면 최종적으로 결과를 저장할 딕셔너리 result에 key값을 split()[0]으로 하여 해당 횟수를 증가했다.

이렇게 한 결과, 놀랍게도 통과가 되었다

def solution(id_list, report, k):
    id_report = {i:0 for i in id_list}  ## 신고 수 받을 딕셔너리 생성
    result = {i:0 for i in id_list}  ## 신고 수 받을 딕셔너리 생성
    report = list(set(report))        ## 중복 제거하기
    #print(id_report)

    # 신고수 적립하기
    for user in report:
        id_report[(user.split())[1]] += 1    ## 신고자-불량이용자

    ## 조건 확인해주기
    r= []
    for x, y in id_report.items():
        ##if k > y:       ## 조건 미달이면 해당 key의 값 0으로 만들기
        ##    id_report[x] = 0
        if k <= y:
            r.append(x)     ## 해당되는 key값, 불량id만 추가

    ## 신고 메일 결과 도출
    for i in report:
        if i.split()[1] in r:
            result[i.split()[0]] += 1
    ##for key, value in id_report.items():
    ##    if value != 0:       ## 정지된 유저
      ##      for i in report:
      ##          if i.split()[1] == key:       ## 불량자가 value라면
       ##             result[i.split()[0]] += 1

    ## value값만 넣기
    return [i for i in result.values()]

print(solution(["muzi", "frodo", "apeach", "neo"], ["muzi frodo","apeach frodo","frodo neo","muzi neo","apeach muzi"], 2))

성공!

 

 

다른 풀이

answer를 id_list의 길이만큼의 배열로 만들었고, 신고당한 id에 대한 신고 횟수를 저장하기 위해 딕셔너리를 이용했다.

(1) 신고 횟수 구하기

중복을 제거하기 위해 set()을 이용하였고, 나의 풀이처럼 신고당한 유저가 key값, 신고당한 횟수를 value값으로 딕셔너리 reprots에 추가했다.

 

(2) 조건 확인 후 정지당한 메일 보낼 아이디 구하기

신고당한 유저의 신고당한 횟수가 k번 이상인지를 확인하기 위해, (2) 번과 비슷하게 딕셔너리 reprots에서 r.split()[1]을 key값으로 판별한다.

그 후, 조건에 맞다면. r.split()[0] 즉, 신고한 사람의 인덱스를 id_list에서 찾아 해당 위치의 answer 리스트 자리에서 +1 해준다.

 

내 풀이와 비교했을 때, 결과를 반환할 딕셔너리를 하나 더 만들지 않은 것과 (2) 번, (3) 번을 합쳐서 특히 index()를 이용해서 결과를 도출한 점이 정말 좋았다.

def solution(id_list, report, k):
    answer = [0] * len(id_list)    
    reports = {x : 0 for x in id_list}

    for r in set(report):
        reports[r.split()[1]] += 1

    for r in set(report):
        if reports[r.split()[1]] >= k:
            answer[id_list.index(r.split()[0])] += 1

    return answer

 

 

 

 

느낀 점

사실 이 문제는 작년에 아무것도 모르고 코테를 지원했을 때 봤던 문제였다. 그때도 앞 문제였던 걸로 기억하는데, 그 당시 백준도 프로그래머스도 안 풀고 정말 코테 준비를 아무것도 하지 않던 나는 '카카오는 역시 어렵구나' 하고 40분쯤 고민하다 '나는 아니구나' 하고 빠르게 나왔던 문제였다. 그래서 사실 Lv1 문제들을 보면서 첫 페이지부터 이 문제가 있는 걸 보고, 아닌 그때 그 문제가 레벨 1이라고? 했었다. 그래서 거의 방치하듯이? 외면하고 있다가 지금까지 푼 문제들 중에 이런 패턴이었다가 혼자 스스로 풀었던 적이 있어 한 번 도전해봤다. 물론 빠르게 풀지 못하고 시간이 걸렸지만, 결국에는 내가 혼자 풀었다! 야호!

그래서 좀 많이 뿌듯했다. 

아직도 갈 길이 멀지만, 중간중간 이런 성취감으로 더 열심히 해야겠다는 마음이 든다.

'코테 문제 > 프로그래머스' 카테고리의 다른 글

[Python] Lv1. 체육복  (0) 2022.04.17
[Python] Lv1.모의고사  (0) 2022.04.17
[Python] Lv1.예산  (0) 2022.04.13
[Python] Lv1.3진법 뒤집기  (0) 2022.04.13
[Python] Lv1.이상한 문자 만들기  (0) 2022.04.13