[Java] 신고 결과 받기 Lv1

Date:     Last Updated:

신고 결과 받기

문제 프로그래머스 FAQ 내용을 보니 카카오나 기업의 문제들은 저작권이 있으니 풀이를
github나 개인블로그에 올리지 말라고 되어 있어서 링크로 대체합니다.

출처: Programmers
신고 결과 받기

문제의 이해

조건과 데이터 정리

-조건

  1. 각 유저는 한번에 한명의 유저만 신고 가능. 1-1. 신고 횟수 제한 없음. 서로 다른 유저 계속 신고 가능. 1-2. 한 유저 여러번 신고도 가능. 다만 신고 횟수는 1회로 처리.
  2. K번 이상 신고 된 유저는 이용정지. 해당 유저를 신고한 모든 유저에게 정지 사실 메일 발송 됨. 2-1. 유저들이 신고한 내용을 취합하여서 마지막에 이용정지를 시키면서 메일 발송.

조건 중에서 2번은 정답이고, 1번은 코드에서 처리 해야 될 부분

-주어진 데이터

  1. 모든 유저의 아이디 String[] id_list 1-1. 정답 출력시 id_list에 넘어온 순서대로 출력하면 됨
  2. 유저들이 신고한 모든 신고 내용 String[] report 2-1. 위의 조건처럼 한 유저를 계속 신고하는 것이 가능함. 다만 신고 횟수는 1번으로 침
  3. 신고 중첩으로 정지를 당하는 횟수 int K

내가 생각한 풀이 방법

  1. 유저 클래스를 만들어 각 유저가 정보를 가지고 있게 할 예정
    1
    2
    3
    4
    5
    6
    
     class UserData{
         String name;        //현재 유저 이름
         UserData[] report;  //현재 유저가 신고한 모든 유저의 객체 배열
         int reported;       //이 유저가 받은 신고 횟수
         int mail;           //신고한 유저 중 정지되는 유저의 수
     }
    
  2. id_list 정보를 가지고 UserData class로 모든 유저 생성
  3. report의 정보로 1) 누가 누구를 신고했는지 각 유저의 UserData에 객체로 저장 2) 해당 유저가 신고받은 횟수를 reported에 저장 3) 여기서 중복된 신고라면 1),2) 모두 실행하지 않음
  4. 유저 클래스에 있는 “내가 신고한 유저” report 배열을 순회하며 해당 유저가 받은 신고 횟수가 k를 넘었는지 체크
    • 값이 k를 넘었다면 mail변수 1 증가
  5. 모든 유저의 mail 변수 값을 answer에 저장&출력 끝

풀이

첫번째 풀이

첫번째 시도는 배열로만 풀기로 했다. 다만 예상은 했지만 역시나 중복 + 반복문 과다 사용 + 동적할당을 실시간으로 할 수 없어서 계속해서 재할당하는 방식으로 했어서 코드의 더러움과 너무 많은 반복문으로 첫 테스트 용도로만 사용했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
//유저 클래스
class UserData{
    String name = "";           //유저의 이름
    UserData report[] = null;  //신고한 유저 객체배열
    int reported = 0;           //이 유저가 받은 신고 횟수
    int mail = 0;               //이 유저가 신고해서 정지 되는 유저의 수
}

class Solution {
    public int[] solution(String[] id_list, String[] report, int k) {
        int[] answer = new int[id_list.length];

        UserData totalUser[] = new UserData[id_list.length];
        String noRepeatReport[];
        int reportSize = 0;

        //이 블럭 부분이 중복 제거 report배열 만드는 부분
        ///////////////////////////////////////////////////////////////////////
        for (int i = 0; i < report.length - 1; i++) {   //report에 들어있는 중복 신고를 제거 해서 새로운 배열에 담음
            for (int j = i + 1; j < report.length; j++) {
                if (report[i].equals(report[j])) {
                    report[j] = "";
                }
            }
        }
        //위에서 중복된 부분을 제거하고 남아 있는 부분만 새로 할당
        for (int i = 0; i < report.length; i++) {
            if (!report[i].equals("")) {
                reportSize++;
            }
        }
        
        //중복 제거 된 repeat 배열을 새로 할당한 배열에 저장
        noRepeatReport = new String[reportSize];
        for (int i = 0; i < report.length; i++) {
            if(!report[i].equals("")){
                noRepeatReport[i] = report[i];
            }
        }
        ///////////////////////////////////////////////////////////////////////

        //모든 유저의 객체 생성 완료
        for (int i = 0; i < id_list.length; i++) {  //만약 동적으로 조절 가능한 자료형(예를 들어 ArrayList)을 쓰면 이런 반복문은 필요 없으나, 공부니까 배열로 함
            totalUser[i] = new UserData();
            totalUser[i].name = id_list[i];
        }

        for (int i = 0; i < totalUser.length; i++) {  //이제 repeat의 정보로 누가 누구를 신고 했는지 신고한 사람의 report 객체에 신고 받은 사람을 넣어주면 됨
            for (int j = 0; j < noRepeatReport.length; j++) {
                String splitString[] = noRepeatReport[j].split(" ");
                if (totalUser[i].name.equals(splitString[0])) {
                    for (int l = 0; l < totalUser.length; l++){
                        if (totalUser[l].name.equals(splitString[1])) {

                            //계속 말하지만 동적할당이 안되어서 이렇게 계속 첫번째와 그 이후에 재할당 하는 코드가 만들어짐
                            if(totalUser[i].report == null){
                                totalUser[i].report = new UserData[1];
                                totalUser[i].report[0] = totalUser[l];
                            }
                            else{
                                UserData tempString[] = totalUser[i].report;
                                totalUser[i].report = new UserData[totalUser[i].report.length + 1];
                                for(int x = 0; x < tempString.length; x++){
                                    totalUser[i].report[x] = tempString[x];
                                }
                                totalUser[i].report[totalUser[i].report.length - 1] = totalUser[l];
                            }
                            totalUser[l].reported++;
                        }
                    }
                }
            }
        }
        
        //mail을 누가 몇번 받는지 계산
        for (int i = 0; i < totalUser.length; i++) {
            if (totalUser[i].report == null) {
                continue;
            }
            for (int j = 0; j < totalUser[i].report.length; j++) {
                if (totalUser[i].report[j].reported >= k) {
                    totalUser[i].mail++;
                }
            }
            answer[i] = totalUser[i].mail;
        }
        return answer;
    }
}

테스트 결과는 실패 런타임오류와 시간초과가 한번에 나왔다. 런타임오류는 프로그래머스의 테스트로는 내가 고칠 방법이 없어서 일단 코드를 정리해 보는 식으로 넘어갔다.

두번째 풀이

불필요한 코드 부분을 줄이기 위해서 ArrayList로 동적할당이 가능하게 해서 코드를 조금 줄여보았다. 시간초과는 동일하지만 런타임 오류는 없어졌다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import java.util.ArrayList;

//유저 클래스
class UserData{
    String name = "";           //유저의 이름
    ArrayList<UserData> report = null;  //신고한 유저 객체배열
    int reported = 0;           //이 유저가 받은 신고 횟수
    int mail = 0;               //이 유저가 신고해서 정지 되는 유저의 수
}

public int[] solution(String[] id_list, String[] report, int k) {
    int[] answer = new int[id_list.length];

    UserData totalUser[] = new UserData[id_list.length];
    ArrayList<String> noRepeatReport = new ArrayList<String>();

    //이 블럭 부분이 중복 제거 report배열 만드는 부분
    ///////////////////////////////////////////////////////////////////////
    for (int i = 0; i < report.length - 1; i++) {   //report에 들어있는 중복 신고를 제거 해서 새로운 배열에 담음
        for (int j = i + 1; j < report.length; j++) {
            if (report[i].equals(report[j])) {
                report[j] = "";
            }
        }
    }
    //위에서 중복된 부분을 제거하고 남아 있는 부분만 새로 할당
    for (int i = 0; i < report.length; i++) {
        if (!report[i].equals("")) {
            noRepeatReport.add(report[i]);
        }
    }
    
    ///////////////////////////////////////////////////////////////////////

    //모든 유저의 객체 생성 완료
    for (int i = 0; i < id_list.length; i++) {  //만약 동적으로 조절 가능한 자료형(예를 들어 ArrayList)을 쓰면 이런 반복문은 필요 없으나, 공부니까 배열로 함
        totalUser[i] = new UserData();
        totalUser[i].report = new ArrayList<>();
        totalUser[i].name = id_list[i];
    }

    for (int i = 0; i < totalUser.length; i++) {  //이제 repeat의 정보로 누가 누구를 신고 했는지 신고한 사람의 report 객체에 신고 받은 사람을 넣어주면 됨
        for (int j = 0; j < noRepeatReport.size(); j++) {
            String splitString[] = noRepeatReport.get(j).split(" ");
            if (totalUser[i].name.equals(splitString[0])) {
                for (int l = 0; l < totalUser.length; l++){
                    if (totalUser[l].name.equals(splitString[1])) {
                        totalUser[i].report.add(totalUser[l]);  //repeat 배열에서 받아온 정보로 각 유저에게 자신이 신고한 유저의 객체를 전달해줌.
                        totalUser[l].reported++;
                    }
                }
            }
        }
    }
    
    //mail을 누가 몇번 받는지 계산
    for (int i = 0; i < totalUser.length; i++) {
        if (totalUser[i].report == null) {
            continue;
        }
        for (int j = 0; j < totalUser[i].report.size(); j++) {
            if (totalUser[i].report.get(j).reported >= k) {
                totalUser[i].mail++;
            }
        }
        answer[i] = totalUser[i].mail;
    }
    return answer;
}

세번째 풀이

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import java.util.ArrayList;
import java.util.HashMap;

//유저 클래스
class UserData{
    String name = "";           //유저의 이름
    ArrayList<UserData> report = null;  //신고한 유저 객체배열
    int reported = 0;           //이 유저가 받은 신고 횟수
    int mail = 0;               //이 유저가 신고해서 정지 되는 유저의 수
}
class Solution{
    public int[] solution(String[] id_list, String[] report, int k) {
        int[] answer = new int[id_list.length];

        UserData totalUser[] = new UserData[id_list.length];
        ArrayList<String> noRepeatReport = new ArrayList<String>();

        //이 블럭 부분이 중복 제거 report배열 만드는 부분
        ///////////////////////////////////////////////////////////////////////
        for (int i = 0; i < report.length - 1; i++) {   //report에 들어있는 중복 신고를 제거 해서 새로운 배열에 담음
            for (int j = i + 1; j < report.length; j++) {
                if (report[i].equals(report[j])) {
                    report[j] = "";
                }
            }
        }
        //위에서 중복된 부분을 제거하고 남아 있는 부분만 새로 할당
        for (int i = 0; i < report.length; i++) {
            if (!report[i].equals("")) {
                noRepeatReport.add(report[i]);
            }
        }
        
        ///////////////////////////////////////////////////////////////////////

        HashMap<String, Integer> totalUser = new HashMap<>();
        HashMap<String, Integer> reported = new HashMap<>();
        //모든 유저의 객체 생성 완료
        for (int i = 0; i < id_list.length; i++) {  //만약 동적으로 조절 가능한 자료형(예를 들어 ArrayList)을 쓰면 이런 반복문은 필요 없으나, 공부니까 배열로 함
            totalUser[i] = new UserData();
            totalUser[i].report = new ArrayList<>();
            totalUser[i].name = id_list[i];
            totalUser.put(id_list[i], 0);
        }
        
        Integer temp = 0;
        for (int i = 0; i < noRepeatReport.size(); i++) {
            String splitString[] = noRepeatReport.get(i).split(" ");
            if (reported.containsKey(splitString[1])) {
                temp = reported.get(splitString[1]);
                reported.put(splitString[1], ++temp);
            }
            else{
                reported.put(splitString[1], 1);
            }
        }
        for (int i = 0; i < noRepeatReport.size(); i++) {
            String splitString[] = noRepeatReport.get(i).split(" ");
            if (totalUser.containsKey(splitString[0])) {
                if (reported.get(splitString[1]) >= k) {
                    temp = totalUser.get(splitString[0]);
                    totalUser.put(splitString[0], ++temp);
                }
            }
        }
        
        //mail을 누가 몇번 받는지 계산
        for (int i = 0; i < id_list.length; i++) {
            answer[i] = totalUser.get(id_list[i]);
        }
        return answer;
    }
}

런타임오류는 해결했고, 시간초과를 해결하면 된다. 일단 실행해보자는 마음에 실행을 한 코드이긴 하지만 다시보니 hashtable 자료구조를 코드에 넣기만 했지 전혀 활용한 부분이 없었다. 중복을 제거하는 반복문도 그대로였다.

네번째 풀이

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import java.util.HashMap;

    public int[] solution(String[] id_list, String[] report, int k) {
        int[] answer = new int[id_list.length];

        HashMap<String, Integer> totalUser = new HashMap<>();   //전체 유저
        HashMap<String, Integer> repeat = new HashMap<>();      //중복을 제거한 신고목록
        HashMap<String, Integer> reported = new HashMap<>();    //전체 유저의 신고당한 횟수
        Integer temp = 0;

        //모든 유저의 객체 생성 완료
        for (int i = 0; i < id_list.length; i++) {  //만약 동적으로 조절 가능한 자료형(예를 들어 ArrayList)을 쓰면 이런 반복문은 필요 없으나, 공부니까 배열로 함
            totalUser.put(id_list[i], 0);
        }
        
        //중복값 제거
        //위에서 반복문으로 처리 했던 걸 자료구조를 통해서 처리
        for (int i = 0; i < report.length; i++) {   
            repeat.put(report[i], 1);
        }

        //반복을 제거한 신고목록을 전체 순회하여
        //신고당한 유저의 신고횟수를 올려줌
        for (String key : repeat.keySet()) {            //해당 유저가 얼마나 신고를 받았는지 체크하는 부분
            String splitString[] = key.split(" ");      //"xxx yyy" 여기서 신고를 한 유저는 xxx 신고를 받은 유저는 yyy split으로 공백을 기준으로 나눠서 저장
            if (reported.containsKey(splitString[1])) { //신고목록에서 신고당한 yyy를 hashmap에서 찾아서 없으면 신고당한 횟수를 1회로 해서 hashmap에 생성(else부분)
                temp = reported.get(splitString[1]);    //존재한다면 현재 값을 불러와서 +1해준다음 다시 put(동일한 key를 put하면 해당 key의 value가 바뀐다.)
                reported.put(splitString[1], ++temp);
            }
            else{
                reported.put(splitString[1], 1);
            }
        }


        //위에서 저장한 신고목록을 활용하여 정답 도출
        for (String key : repeat.keySet()) {                //위에서 얼마나 신고 받았는지 정리한 것을 k값과 비교해서 정지인지 아닌지 판별
            String splitString[] = key.split(" ");          //"xxx yyy" 여기서 신고를 한 유저는 xxx 신고를 받은 유저는 yyy split으로 공백을 기준으로 나눠서 저장
            if (totalUser.containsKey(splitString[0])) {    //신고목록에서 신고한 xxx hashmap에서 찾아서 없으면 pass
                if (reported.get(splitString[1]) >= k) {    //존재한다면 위에서 저장한 xxx가 신고한 사람 yyy이 신고 당한 횟수가 k값을 넘을경우 현재 xxx가 정지메일을 받음
                    temp = totalUser.get(splitString[0]);   //그러므로 xxx가 받는 메일 횟수를 +1증가시켜서 저장시켜줌
                    totalUser.put(splitString[0], ++temp);
                }
            }
        }
        
        //mail을 누가 몇번 받는지 계산
        //이 부분이 필요한 이유는 hashmap은 순서가 없다보니 hashmap출력시 내가 원하는 순서대로 출력이 되지 않는다.
        //그래서 처음 주어진 전체 유저 목록은 순서대로이기 때문에 그것을 이용해서 answer에 순서대로 저장
        for (int i = 0; i < id_list.length; i++) {
            answer[i] = totalUser.get(id_list[i]);
        }
        return answer;
    }

성공한 풀이. 다만 실제 푼것은 5월20일이었는데 그때 너무 피곤해서 (너무 오랜만이라서 그런지 푸는데 12시간 걸리더라, 물론 계속 방식을 바꾼 이유도 있겠지만…) 5월22일에 성공 풀이를 보니 고칠점이 더 많더라. 반복문을 더 줄일 수 있을 거 같고, 변수나 코드도 줄일 수 있을 것같다. 여튼 hashmap을 이용해서 처음 하려던 UserClass를 지우고 좀 더 간단하게, 자료구조에서 지원해주는 기능을 이용해서 풀었다.

Lv1인데 이렇게 어렵게 풀어야 될 문제인가.. 약간 자괴감이 들긴했다. 연습하면 나아지겠지~ 이 문제의 질문하기 코너 보면 이거 Lv1문제 맞나요? 라는 댓글을 봤는데.. 그게 조금은 마음의 위안이 되었다……….


Leave a comment