[프로그래머스] 신고 결과 받기

DYKO·2023년 1월 8일
0

코딩 테스트

목록 보기
1/3

🔗 프로그래머스 문제로 이동하기

[문제 설명]

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

  • 각 유저는 한 번에 한 명의 유저를 신고할 수 있습니다.
    • 신고 횟수에 제한은 없습니다. 서로 다른 유저를 계속해서 신고할 수 있습니다.
    • 한 유저를 여러 번 신고할 수도 있지만, 동일한 유저에 대한 신고 횟수는 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 순서대로 각 유저가 받은 결과 메일 수를 담으면 됩니다.

[제출 답안]

import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;

class Solution {
    public int[] solution(String[] id_list, String[] report, int k) {
        int[] answer = new int[id_list.length];
        
        Map<String, ReportInfo> reportedInfo = getReportInfo(report);
        List<String> idList = java.util.Arrays.asList(id_list);
        
        for(ReportInfo info : reportedInfo.values()) {
            if (info.getReportedCnt() >= k){
                List<String> reportingUser = info.getReportingUser();
                
                for (String reportingUserId : reportingUser){
                    int idIdx = idList.indexOf(reportingUserId);
                    answer[idIdx]++;
                }
            }
        }
        
        return answer;
    }
    
    private Map<String, ReportInfo> getReportInfo(String[] report) {
        Map<String, ReportInfo> reportInfoMap = new HashMap<>();
        
        for(String item : report) {
            String reportingUser = item.split(" ")[0];
            String reportedUser = item.split(" ")[1];
            
            if (!reportInfoMap.containsKey(reportedUser)){
                ReportInfo reportInfo = new ReportInfo(reportedUser);
                reportInfo.reported(reportingUser);
                
                reportInfoMap.put(reportedUser, reportInfo);
            } else {
                reportInfoMap.get(reportedUser).reported(reportingUser);
            }
        }
        
        return reportInfoMap;
    }
        
    
    private class ReportInfo {
        private String id;
        private List<String> reportingUser;
        
        public ReportInfo (String id) {
            this.id = id; 
            this.reportingUser = new ArrayList<>(); 
        }
        
        public String getId() { return this.id; }
        public int getReportedCnt() { return this.reportingUser.size(); }
        public List<String> getReportingUser() { return this.reportingUser; }
        
        public void reported(String reportingUserId){
            if(!includeReportingUser(reportingUserId)) { 
                this.reportingUser.add(reportingUserId); 
            }
        }
        
        public boolean includeReportingUser(String reportingUserId) {
            return this.reportingUser.contains(reportingUserId);
        }
    }
}

고려한 점
1. 객체지향적 코드
2. Map을 사용해 사용자 별 신고 정보 정리
3. List를 이용해 answer 의 사용자의 index 지정


[ 다른 사용자 답안 유형 ]

1. Stream 활용 코드

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

class Solution {
    public int[] solution(String[] id_list, String[] report, int k) {
        List<String> list = Arrays.stream(report).distinct().collect(Collectors.toList());
        HashMap<String, Integer> count = new HashMap<>();
        for (String s : list) {
            String target = s.split(" ")[1];
            count.put(target, count.getOrDefault(target, 0) + 1);
        }

        return Arrays.stream(id_list).map(_user -> {
            final String user = _user;
            List<String> reportList = list.stream().filter(s -> s.startsWith(user + " ")).collect(Collectors.toList());
            return reportList.stream().filter(s -> count.getOrDefault(s.split(" ")[1], 0) >= k).count();
        }).mapToInt(Long::intValue).toArray();
    }
}

간결하고 깔끔한 코드, 그러면서도 화려하다!!
”이게 정녕 내가 배운 자바가 맞는건가…”라는 댓글에 십분 공감한다. 다만, 성능면에서 중요한 Application의 경우 Stream을 사용해도 되는 지 고민이 필요해 보인다.


2. 객체지향적 코드

import java.util.*;

class Solution {
    public int[] solution(String[] id_list, String[] report, int k) {
        int[] answer = new int[id_list.length];
        ArrayList<User> users = new ArrayList<>();
        HashMap<String,Integer> suspendedList = new HashMap<>(); //<이름>
        HashMap<String,Integer> idIdx = new HashMap<String,Integer>(); // <이름, 해당 이름의 User 클래스 idx>
        int idx = 0;

        for(String name : id_list) {
            idIdx.put(name,idx++);
            users.add(new User(name));
        }

        for(String re : report){
            String[] str = re.split(" ");
            //suspendedCount.put(str[0], suspendedCount.getOrDefault(str[0],0)+1);
            users.get( idIdx.get(str[0])).reportList.add(str[1]);
            users.get( idIdx.get(str[1])).reportedList.add(str[0]);
        }

        for(User user : users){
            if(user.reportedList.size() >= k)
                suspendedList.put(user.name,1);
        }

         for(User user : users){
             for(String nameReport : user.reportList){
                 if(suspendedList.get(nameReport) != null){
                     answer[idIdx.get(user.name)]++;
                 }

             }
        }
        
        return answer;
    }
}

class User{
    String name;
    HashSet<String> reportList;
    HashSet<String> reportedList;
    public User(String name){
        this.name = name;
        reportList = new HashSet<>();
        reportedList = new HashSet<>();
    }
}

나와 비슷하게 Class 를 활용하여 객체지향적으로 문제를 풀은 케이스.
하지만 속도 면에서 좀 더 뛰어나다. 어느 부분에서 차이가 있는지 분석할 필요성이 있다.


3. for문과 Map을 활용한 코드

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

class Solution {
    public int[] solution(String[] id_list, String[] report, int k) {
        int[] answer = new int[id_list.length];
        Map<String, Integer> idIndex = new HashMap<>();
        Map<String, List<String>> reportMap = new HashMap<>();

        for (int i = 0; i < id_list.length; i++) {
            idIndex.put(id_list[i], i);
            reportMap.put(id_list[i], new ArrayList<>());
        }

        for (String reported : report) {
            String[] temp = reported.split(" ");
            if (!reportMap.get(temp[1]).contains(temp[0])) {
                reportMap.get(temp[1]).add(temp[0]);
            }
        }

        for (String id : reportMap.keySet()) {
            if (k <= reportMap.get(id).size()) {
                for (String reporter : reportMap.get(id)) {
                    answer[idIndex.get(reporter)]++;
                }
            }
        }

        return answer;
    }
}

간결 그 자체, 누가 봐도 이해하기 쉽다. 그러면서도 성능면에서 우월하게 강세를 보이는 코드다.
간소한 동작에는 복잡한 구조보다 차라리 직관적인 풀이가 더 유용할 수 있음을 깨닫는다.


사담

Level 1 테스트인데도 불구하고 처음에는 꽤나 버벅였다... 게다가 다른 사람들이 푼 문제를 보고 꽤나 충격이었다. 어렴풋이 알고는 있었는데, 꽤나 내가 오래된 자바 코딩 스타일이라는걸 눈으로 직접 확인하게 됐다. 그리고 알고리즘... 다시 공부 해야지..ㅠ

profile
엔지니어가 되는 그 날 까지!

0개의 댓글