# 각 유저는 한 번에 한 명의 유저를 신고할 수 있습니다.
# 신고 횟수에 제한은 없습니다. 서로 다른 유저를 계속해서 신고할 수 있습니다.
# 한 유저를 여러 번 신고할 수도 있지만, 동일한 유저에 대한 신고 횟수는 1회로 처리됩니다.
# k번 이상 신고된 유저는 게시판 이용이 정지되며, 해당 유저를 신고한 모든 유저에게 정지 사실을 메일로 발송합니다.
# 유저가 신고한 모든 내용을 취합하여 마지막에 한꺼번에 게시판 이용 정지를 시키면서 정지 메일을 발송합니다.
id_list = ["muzi", "frodo", "apeach", "neo"]
id_list2 = ["con", "ryan"]
report = ["muzi frodo","apeach frodo","frodo neo","muzi neo","apeach muzi"]
report2 = ["ryan con", "ryan con", "ryan con", "ryan con"]
k = 2
k2 = 3
# 신고 성공이메일
# [2,1,1,0]
# [0,0]
# ['muzi frodo', 'frodo neo', 'apeach muzi', 'apeach frodo', 'muzi neo']
def solution(id_list, report, k):
answer = []
arrTarget = []
arrReport = []
# 중복제거
report = set(report)
report = list(report)
# 배열 두개 나누기 (항상 같은 배열수)
for i in report:
a, b = i.split()
arrReport.append(a)
arrTarget.append(b)
# 정지기준을 채운사람은 누구인가?
arr1 = []
for i in id_list:
count = arrTarget.count(i)
if (count >= k):
arr1.append(i)
# 이사람을 신고한 사람은 누구인가?
n = 0
arr2 = []
while n < len(arrReport):
for i in arr1:
if (arrTarget[n] == i) :
arr2.append(arrReport[n])
n = n + 1
for i in id_list:
count = arr2.count(i)
answer.append(count)
return answer
# 결과 = solution(id_list, report, k)
# 결과2 = solution(id_list2, report2, k2)
코딩 스토리
시간이 오래걸린 요인으로는 문제를 보고 무턱대고 코딩을 시작했던 점이었다. 문제를 모두 이해하고 차근차근 로직을 세운 다음에 코딩을 하는 것이 오히려 빨랐다.
처음 접근은 신고자 - 신고대상 관계여서 단순하게 1대 다 관계라고 생각하고 딕셔너리(연관배열)로 접근을 시도했다. 코딩을 하던중 1명이 한사람을 선택하지만 다른사람도 선택할 수 있고 여러번 지목이 안된다는 점에서 이미 딕셔너리 구조를 사용하지 못한다. 딕셔너리 구조는 하나의 키 하나의 값만 가지므로 1대 1 구조로 보는것이 맞았다 이때까지 키와 값으로 구성되어서 1대 다 구조로 생각해왔는데 이것은 잘못 생각했었다.
그럼 딕셔너리는 잘못되었고 다른방법으로는 무엇이 있을까 고민하다 배열을 두개로 만들어서 for 를 돌때 두개의 배열을 같은 인덱스를 지목하도록 유도해서 해결했다. 이문제를 해결하기 위해서는 논리를 이렇게 구성했다.
1. 받은 배열의 중복을 제거하자 (집합으로 변경후 다시 배열로 변환)
- 이부분은 배열을 집합으로 변환했을때 set(배열) 의 특징중 중복이 제거된다는 점을 활용해서 중복을 제거했다
2. 문자열 띄워쓰기로 구분되어진 부분을 두개로 분리하자 (여기까지 사전 빌드업)
- 이번 아이디어의 핵심을 사용하기 위해서는 두개의 배열을 만들어야한다
3. 정지기준을 채운 사람을 반환한다
- 사용자의 리스트 배열을 돌며 그값을 이용해 신고받은 사람의 배열에 접근해 신고기준에 해당되는 사람을 새로운 배열에 담는다
4. 이사람을 신고한 사람을 찾는다
- 신고자의 배열수만큼 while문을 돌며 신고자배열을 돌며 몇번째에 겹치는지 찾는다 신고자의 인덱스를 찾기 위해서
5. 이제 누가 신고한지 알게 되었다 문제에서 받고자하는 리턴 양식으로 만들자
- 사용자의 리스트를 돌며 신고자 리스트를 카운트한다
++
위와같이 풀어서 결과를 받아봤더니 런타임 오류가 나왔다.. 분명 정상작동하는 코드인데 성능이 안나온다는 의미다
결국 다른 사람의 해답을 봤더니 처음 접근한 딕셔너리가 맞았다 하지만 딕셔너리를 100프로 이해하고 있던게 아니었다
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 # r.split()[1] 나눈후 배열인덱스 불러오는거처럼 나눈 값을 가져올수있음 (기억하기)
# 신고한 인원 카운트
for r in set(report):
if reports[r.split()[1]] >= k: # 신고기준에 맞는지 확인
answer[id_list.index(r.split()[0])] += 1 # 신고기준에 맞다면 id_list에 검색해서 그에맞는 인덱스를 받아서 해당하는 위치에 +1
return answer
신고를 받은 인원을 딕셔너리로 만들어 관리하고 마지막에 신고기준에 맞는 배열의 첫번째를 배열에 직접 던져서 찾고 인덱스를 받아서 증가한다 여기서 시간 감축의 핵심은 뭐니뭐니해도 id_list를 미리 사전에 길이만큼 0을 깔아두는 점이다 이렇게되면 인덱스만 받아서 더하면 되니까 찾는 시간에서 확 줄어든다.