알고리즘/Java

Programmers: 신고 결과 받기(2022 KAKAO BLIND RECRUITMENT)

두넌 2023. 10. 12.

문제 정보


 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

핵심


String 을 얼마나 잘 다루는지에 대하여, 또한 다양한 Data Type을 활용하여 연산할 수 있는지 판단하는 듯

또한 더 나아가 Java의 Stream API를 얼마나 잘 활용하는 지도 볼 수 있을 것 같다

 

풀이


import java.util.*;

class Solution {
    public int[] solution(String[] id_list, String[] report, int k) {
        int[] answer = {};
        Map<String, Integer> userInfo = new HashMap<>();
        Map<Integer, Set<Integer>> userReport = new HashMap<Integer, Set<Integer>>();
        for(int i=0; i<id_list.length; i++) {
            userInfo.put(id_list[i], i);
            userReport.put(i, new HashSet<Integer>());
        }
        
        for(String rep: report) {
            String[] splitWords = rep.split(" ");
            userReport.get(userInfo.get(splitWords[0])).add(userInfo.get(splitWords[1]));
        }
        
        int[] repNumber = new int[id_list.length];
        for(int i=0; i<id_list.length; i++) {
            for(int repUserNumber: userReport.get(i)) {
                repNumber[repUserNumber]++;
            }
        }
        
        Set<Integer> closedUser = new HashSet<>();
        for(int i=0; i<repNumber.length; i++) {
            if (repNumber[i] >= k)
                closedUser.add(i);
        }
        
        answer = new int[id_list.length];
        for(int i=0; i<id_list.length; i++) {
            for(int rep: userReport.get(i)) {
        		if(closedUser.contains(rep))
            		answer[i]++;
            }
        }
        
        return answer;
    }
}

Specification은 다음과 같다

- Map<String, Integer> userInfo: id_list에 존재하는 user의 이름과 No. 를 저장하는 Map

- Map<Integer, Set<Integer>> userReport: id_list에 존재하는 user의 No. 와 그 user가 report한 user의 No. 를 저장하는 Map

- int[] repNumber: index-> user의 No, value-> user가 신고당한 횟수

- Set<Integer> closedUser: k번 이상 신고된 user의 No가 담긴 Set

 

동작 알고리즘은 다음과 같다

1. userInfo에 user의 이름과 해당 user의 index(No.)를 저장한다

2. userReport에 신고한 user에 해당하는 Set에 신고당한 user의 No. 를 추가한다

2-1. 이 과정에서 동일한 user의 중복된 신고를 필터링할 수 있다

3. repNumber에 해당 user가 몇번 신고되었는지 기록한다

4. k번 이상 신고된 user의 No. 를 closedUser에 저장한다

5. userReport에서 user가 신고한 다른user를 저장한 Set을 돌며 closedUser에 포함되어 있다면 count를 올려 준다

6. 해당 answer를 return한다

 

문제를 풀고, 다른 분의 풀이를 참고하며 Stream API를 활용하여 이를 정리해 보려 한다

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();
    }
}
List<String> list = Arrays.stream(report).distinct().collect(Collectors.toList());

report 배열의 Stream에서 중복된 값을 제거하여 List 형태로 list에 저장한다

 

HashMap<String, Integer> count = new HashMap<>();
        for (String s : list) {
            String target = s.split(" ")[1];
            count.put(target, count.getOrDefault(target, 0) + 1);
        }

count에는 해당 user가 몇번 신고당했는지 저장한다

getOrDefault() 를 통하여, 값이 이미 있는 경우 해당 값에 + 1을 해주고 없는 경우 default(0) 에 + 1 을 해준다

HashMap의 경우 동일한 target에 대한 여러번의 put은 가장 마지막 put의 value만 들어가기 때문에, 이처럼 처리를 해주어야 한다

 

 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();

마지막 대망의 코드인데,

Arrays.stream(id_list).map(_user -> {

다음과 같이 id_list의 stream을 만들고, 아래와 같이 mapping한다

final String user = _user;

이 코드는 _user을 Immutable Reference로 만들어주는 것 같다

List<String> reportList = list.stream().filter(s -> s.startsWith(user + " ")).collect(Collectors.toList());

특정 user + " " 로 시작하는 (report 배열) 문자열만 filtering 하고, 이를 List로 만들어 준다

이 과정을 거쳐 user가 신고한 다른 user의 List가 만들어진다 (ex> ["user1 user2", "user1 user3" ...])

return reportList.stream().filter(s -> count.getOrDefault(s.split(" ")[1], 0) >= k).count();

count.getOrDefault(s.split(" ")[1], 0) 을 통하여 신고한 다른 user가 신고된 횟수를 가져올 수 있다, default는 0이다

filter( s -> ... >= k) 를 통해 위 신고된 횟수의 count가 k보다 큰 경우에 해당하는 것만 filtering하고

.count() 를 통하여 신고한 유저가 k번 이상 신고되었을 때 에 해당하는 개수를 counting한다

결론적으로 최상위인 Arrays.stream(id_list).map(_user -> { ... }) 을 시도하여 우리는 id_list의 stream을 index에 해당하는 user가 신고한 user들 중, k번 이상 신고된 user의 명 수가 저장된다

}).mapToInt(Long::intValue).toArray();

마지막으로 이를 Int로 mapping 한다. count()의 return type은 Long인 듯 하다

이를 .toArray() 를 통하여 int[] 로 만들어 준다

Array to List 는 위처럼 .collect(Collectors.toList()) 이다

 

 

고찰


Stream의 활용에 대하여 어느정도 알게 되었던 것 같다

이를 많이 활용해서 잘 다루는 연습을 해야할 것 같다

 

Reference


 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

 

 

Source Code on GitHub


GitHub에서 소스코드 보기

 

 

댓글