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

 

코딩테스트 연습 - 괄호 변환 | 프로그래머스

카카오에 신입 개발자로 입사한 콘은 선배 개발자로부터 개발역량 강화를 위해 다른 개발자가 작성한 소스 코드를 분석하여 문제점을 발견하고 수정하라는 업무 과제를 받았습니다. 소스를 컴파일하여 로그를 보니 대부분 소스 코드 내 작성된 괄호가 개수는 맞지만 짝이 맞지 않은 형태로 작성되어 오류가 나는 것을 알게 되었습니다. 수정해야 할 소스 파일이 너무 많아서 고민하던 콘은 소스 코드에 작성된 모든 괄호를 뽑아서 올바른 순서대로 배치된 괄호 문자열을 알려주는

programmers.co.kr

문제가 복잡해 보이지만 정말 시키는 대로만 재귀로 잘 구현하면 된다. 재귀로 구현해야하는 부분도 문제에 잘 나와있다.

 

올바른 괄호 문자열인지 검사하는 부분만 스택을 이용해서 따로 구현했다.

문자열의 모든 문자들에 대해서 여는 괄호면 스택에 넣어주고 닫는 괄호면 스택에서 빼준다. 이때, 스택이 비어있다면 올바른 괄호 문자열이 아니므로 false를 바로 리턴한다.

 

문자열 검사가 끝났는데 스택에 아직 괄호가 남아있다면 그것도 올바른 괄호 문자열이 아니므로 false를 리턴한다.

위에서 false를 리턴하지 않았다면 올바른 괄호 문자열이므로 true를 리턴한다.

 

 

각 조건을 구현한 부분에 주석을 달아놨다.

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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#include <string>
#include <vector>
#include <stack>
using namespace std;
 
//올바른 괄호 문자열인지 검사
bool isok(string s) {
    stack<char> st;
 
    for (char c : s) {
        if (c == '(') {
            st.push(c);
        }
        else if (c == ')') {
            if (st.empty()) {
                return false;
            }
            else {
                st.pop();
            }
        }
    }
 
    if (st.empty()) return true;
    else return false;
 
}
 
 
string seperate(string p) {
    if (p == ""return "";
 
    string u = "";
    string v = "";
    string result = "";
 
    int cnt1 = 0;
    int cnt2 = 0;
    int index = 0;
 
    //2번 조건
    for (char c : p) {
        if (c == '(') {
            cnt1++;
        }
        else {
            cnt2++;
        }
        index++;
 
        //균형잡힌 상태가 되면 바로 break(더 분리할 수 없도록);
        if (cnt1 == cnt2) break;
    }
 
    //u와 v로 나누다. 
    u += p.substr(0, index);
    v += p.substr(index);
 
 
    //3번 조건
    if (isok(u)) {
        result += u;
        result += seperate(v);
    }
    else {
        //4번 조건
 
        //4-1
        result = "(";
        //4-2
        result += seperate(v);
        //4-3
        result += ")";
 
        //4-4
        u = u.substr(1, u.size() - 2);
        int len = u.size();
        for (int i = 0; i<len; i++) {
            if (u[i] == '(') {
                result += ")";
            }
            else {
                result += "(";
            }
        }
    }
 
 
    //4-5
    return result;
 
}
 
 
string solution(string p) {
    string answer = "";
    
    //1번 조건
    if (isok(p)) return p;
 
 
    answer = seperate(p);
 
 
    return answer;
}
Colored by Color Scripter
 

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

 

코딩테스트 연습 - 문자열 압축 | 프로그래머스

데이터 처리 전문가가 되고 싶은 어피치는 문자열을 압축하는 방법에 대해 공부를 하고 있습니다. 최근에 대량의 데이터 처리를 위한 간단한 비손실 압축 방법에 대해 공부를 하고 있는데, 문자열에서 같은 값이 연속해서 나타나는 것을 그 문자의 개수와 반복되는 값으로 표현하여 더 짧은 문자열로 줄여서 표현하는 알고리즘을 공부하고 있습니다. 간단한 예로 aabbaccc의 경우 2a2ba3c(문자가 반복되지 않아 한번만 나타난 경우 1은 생략함)와 같이 표현할 수

programmers.co.kr

문자열을 자르는 단위를 1부터 시작해서 모두 해보면 된다.

단위가 문자열의 길이의 반보다 커지면 절대 더 압축되지 않으므로 1부터 문자열의 길이/2 까지만 해보면 된다.

 

 

i개로 자른다고 했을 때, 문자열의 처음부터 i개만큼씩을 문자열 끝까지 계속 검사해주면 된다.

(i개로 나누어 떨어지지 않고 남은 부분은 마지막에 추가적으로 붙여준다.)

 

 

i개로 자른 문자열이 바로 이어서 나타난다면 cnt값을 증가시킨다.

그렇지 않다면, 압축된 결과를 저장할 문자열(result)에 문자열 단위를 붙여주고 문자열 단위를 새로 j번부터 i개로 업데이트한다. 이때, 문제 조건에 따라서 이 문자열 단위가 여러 개였다면(cnt가 1 이상이라면) cnt를 문자열 앞에 붙여서 result에 추가하고 cnt는 1로 다시 초기화한다.

 

 

i개로 잘라서 압축한 경우의 문자열을 모두 완성했다면 그때의 길이를 최솟값과 비교한다.

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
#include <string>
#include <vector>
using namespace std;
 
int solution(string s) {
    int len = s.size();
    int answer = len;
 
    int n = len / 2;
 
 
    //i개 단위로 잘라서 압축
    for (int i = 1; i <= n; i++) {
 
        //i개 단위로 잘라서 압축한뒤 만들어지는 문자열
        string result = "";
 
        //자를 문자열 단위
        string ss = s.substr(0, i);
 
        int cnt = 1;
 
        //앞에서 자른 문자열 단위를 제외한 뒷부분(j번 문자부터) 문자열을 검사한다.
        for (int j = i; j <= len; j += i) {
            //j번 부터 i개 만큼이 문자열 단위와 똑같은 경우 cnt 증가
            if (ss == s.substr(j, i)) {
                cnt += 1;
            }
            else {
                //다른 경우 중 cnt가 1이면 result에 그대로 ss를 붙인다.
                if (cnt == 1) {
                    result += ss;
                }
                else {
                    //cnt가 1보다 크다면 cnt를 문자열 단위 앞에 붙여서 result에 이어준다.
                    result += (to_string(cnt) + ss);
                }
 
                //문자열 단위를 j번부터 i개로 변경 
                ss = s.substr(j, i);
                //cnt값 다시 1로 초기화
                cnt = 1;
            }
 
        }
 
        //문자열이 i로 나누어 떨어지지 않는다면 result에 나머지 부분을 붙여줘야한다.
        if ((len%i) != 0) {
            result += s.substr((len / i)*i);
        }
 
        //최솟값을 answer에 저장
        if (answer > result.size()) answer = result.size();
    }
 
 
    return answer;
}
Colored by Color Scripter
 

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

 

코딩테스트 연습 - 소수 찾기 | 프로그래머스

한자리 숫자가 적힌 종이 조각이 흩어져있습니다. 흩어진 종이 조각을 붙여 소수를 몇 개 만들 수 있는지 알아내려 합니다. 각 종이 조각에 적힌 숫자가 적힌 문자열 numbers가 주어졌을 때, 종이 조각으로 만들 수 있는 소수가 몇 개인지 return 하도록 solution 함수를 완성해주세요. 제한사항 numbers는 길이 1 이상 7 이하인 문자열입니다. numbers는 0~9까지 숫자만으로 이루어져 있습니다. 013은 0, 1, 3 숫자가 적힌 종이

programmers.co.kr

문자열로 주어지는 numbers의 길이가 7이하이기 때문에 완전탐색으로 풀 수 있다.

문제 카테고리도 완전 탐색이어서 바로 알 수 있긴하다...

 

1. 먼저 에라토스테네스의 체를 이용해서 소수를 모두 구해놓는다. 숫자 i가 소수라면 check[i]는 false값을 갖는다.

2. numbers의 각 자리의 값을 int형으로 변환하여 벡터에 넣어줬다.

3. 벡터에 넣어준 값들로 만들 수 있는 값들을 모두 구해서 set에 넣어준다(중복을 없애기 위해서)

4. 마지막으로 set에 있는 값들을 검사하여 소수이면 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
#include <string>
#include <vector>
#include <set>
#define MAX 10000000
using namespace std;
 
bool check[MAX+1];
bool used[10];
set<int> st;
vector<int> vt;
 
void getPrime() {
    check[0= true;
    check[1= true;
    for(int i=2; i*i<=MAX; i++) {
        if(check[i]) continue;
        for(int j = i+i; j<=MAX; j+=i) {
            check[j] = true;
        }
    }
}
 
int n;
void select(int index, int num) {
    if(index == n) {
        //n가지 종이조각을 모두 사용하였다.
        return;
    }
    
    int newnum;
    
    //n가지의 종이조각으로 만들 수 있는 모든 수를 구한다.
    for(int i=0; i<n; i++) {
        //i번째 종이 조각을 사용하였는지 검사
        if(used[i]) continue;
        
        //i번째 종이 조각을 사용한다.
        used[i] = true;
 
        //이전의 숫자와 i번째 숫자를 합쳐준다.
        newnum = num*10+vt[i];
        
        //set에 넣고
        st.insert(newnum);
        
        //현재 만들어진 숫자에 다른 숫자를 더 붙이는 경우 탐색
        select(index+1, newnum);
 
        //다른 종이를 선택하는 경우를 위해서 다시 false값으로 변경
        used[i] = false;
    }
}
 
int solution(string numbers) {
    int answer = 0;
    n = numbers.size();
    
    //소수를 찾는다.(소수이면 check배열 값이 true)
    getPrime();
 
    
    //int로 바꿔서 vector에 넣는다.
    for(char c : numbers) {
        vt.push_back(c-'0');
    }
 
    
    //만들 수 있는 숫자 조합을 중복없이 구한다.
    select(0,0);
    
    //set에 들어가있는 값이 소수이면 answer값 증가
    for(auto iter=st.begin(); iter!=st.end(); iter++) {
        if(!check[*iter]) answer++;
    }
    
    return answer;
}
Colored by Color Scripter
 

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

 

코딩테스트 연습 - [1차] 프렌즈4블록 | 프로그래머스

프렌즈4블록 블라인드 공채를 통과한 신입 사원 라이언은 신규 게임 개발 업무를 맡게 되었다. 이번에 출시할 게임 제목은 프렌즈4블록. 같은 모양의 카카오프렌즈 블록이 2×2 형태로 4개가 붙어있을 경우 사라지면서 점수를 얻는 게임이다. 만약 판이 위와 같이 주어질 경우, 라이언이 2×2로 배치된 7개 블록과 콘이 2×2로 배치된 4개 블록이 지워진다. 같은 블록은 여러 2×2에 포함될 수 있으며, 지워지는 조건에 만족하는 2×2 모양이 여러 개 있다면

programmers.co.kr

백준의 Puyo Puyo문제나 SWEA의 벽돌 깨기 문제와 비슷한 문제다.

이 문제에서는 네 칸만 확인해주면 되므로 모든 (i, j) 칸이 바로 오른쪽(j+1, i), 바로 아래쪽(i+1, j), 대각선 아래(i+1, j+1)와 같은지 확인해주면 된다. 이때 범위를 넘어가지 않도록 i는 m-1보다 작을 때까지, j는 n-1보다 작을 때까지만 검사한다. 그리고 지워진 칸에 대해서는 검사하지 않는다.

 

 

네 칸이 모두 같을 때, 해당 칸이 이미 다른 칸으로부터 지워질 수도 있기 때문에 바로 answer값을 증가시키지 않는다. 위의 그림에서 라이언 하나가 중복되어 있는 경우다. check배열을 사용하여 false인 경우에만 answer값을 증가시킨고 check배열의 값을 true로 바꿔준다.

 

 

모든 칸에 대해서 검사를 하는동안 한 번이라도 지워지는 게 있다면 flag변수가 true값을 갖는다.

검사가 끝난 후에 flag변수가 false값을 가지고 있다면 지워지는 게 없다는 뜻이므로 검사를 종료한다.

true값을 가지고 있다면 지워져야하는 블록들이 있다는 뜻이므로 check배열의 값이 true인 블록들을 빈칸으로 만드어준다.

 

 

지워진 블록들이 있다면 이제 떠있는 블록들을 아래로 내려줘야한다.

모든 열에 대해서 각 행들을 밑에서부터 검사해준다. 밑에서부터 빈칸을 찾은 후에 그 빈칸보다 위쪽에 떠있는 블록을 찾아서 해당 빈칸으로 내려주면 된다. ok변수를 사용해서 발견한 빈칸보다 위쪽 칸들이 모두 빈칸일 경우에는 떠있는 블록이 없다는 뜻이므로 다음 열의 검사로 넘어간다.

 

 

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
91
#include <string>
#include <vector>
#include <cstring>
 
using namespace std;
 
bool check[30][30];
 
int solution(int m, int n, vector<string> board) {
    int answer = 0;
 
    while (true) {
 
        bool flag = false;
        memset(check, falsesizeof(check));
 
        for (int i = 0; i<- 1; i++) {
            for (int j = 0; j<- 1; j++) {
 
                if (board[i][j] == ' 'continue;
                if (board[i][j] != board[i][j + 1]) continue;
                if (board[i][j] != board[i + 1][j]) continue;
                if (board[i][j] != board[i + 1][j + 1]) continue;
 
                flag = true;
 
                if (!check[i][j]) {
                    check[i][j] = true;
                    answer++;
                }
 
                if (!check[i][j + 1]) {
                    check[i][j + 1= true;
                    answer++;
                }
 
                if (!check[i + 1][j]) {
                    check[i + 1][j] = true;
                    answer++;
                }
 
                if (!check[i + 1][j + 1]) {
                    check[i + 1][j + 1= true;
                    answer++;
                }
 
 
 
            }
        }
 
 
        //더 지워질게 없다면 종료
        if (!flag) break;
 
 
        //붙어있는 애들 지워준다.
        for (int i = 0; i<m; i++) {
            for (int j = 0; j<n; j++) {
                if (check[i][j]) board[i][j] = ' ';
            }
        }
 
 
        //블록 지워진 후에 아래로 떨어져서 빈공간 채운다.
        for (int j = 0; j<n; j++) {
            for (int i = m - 1; i >= 0; i--) {
                if (board[i][j] != ' 'continue;
 
                bool ok = false;
 
                for (int k = i - 1; k >= 0; k--) {
 
                    if (board[k][j] != ' ') {
                        board[i][j] = board[k][j];
                        board[k][j] = ' ';
                        ok = true;
                        break;
                    }
 
                }
 
                if (!ok) break;
 
            }
        }
 
 
    }
    return answer;
}
Colored by Color Scripter
 

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

 

코딩테스트 연습 - [1차] 다트 게임 | 프로그래머스

 

programmers.co.kr

다트 게임은 총 3번의 기회만 있으므로 각 게임에서의 점수를 저장할 int 배열

arr[3]을 만들어서 마지막에 answer에다가 arr배열의 값(arr[0] , arr[1], arr[2])을 모두 더해줬다.

 

 

점수의 계산해주기 위해서 dartResult의 문자를 하나하나 아래와 같은 경우로 나눠서 검사했다.

S인 경우 / D인 경우 / T인 경우 / *인 경우 / #인 경우 / 그 외인 경우(숫자)

 

S인 경우

앞에 나온 점수 그대로이므로 다음 문자를 검사하기 위해 index만 증가시킨다.

 

D인 경우

점수의 제곱을 해주어야 하므로 현재 점수에 다시 현재 점수를 곱해준 값을 저장하고 다음 문자를 검사하기 위해 index를 증가시킨다.

 

T인 경우

점수의 세제곱이므로 앞의 점수의 세제곱 해준 값을 점수에 다시 저장하고 index를 증가시킨다.

 

*인 경우

현재 점수와 바로 이전의 점수를 2배로 만들어주면 되는데

앞의 보너스(S, D, T)에서 index를 증가시켜버렸으므로 index-1번째(현재 점수)와 index-2번째(이전의 점수)의 값에 2를 곱해주면 된다. 이전의 점수에 2를 곱해줄 때는, 현재 점수가 첫 번째 게임인 경우 이전의 점수가 없으므로 index-2가 0보다 크거나 같은지 검사를 해주고 나서 계산해주어야 한다.

(밑의 코드에서 곱하기 연산 대신에 shift연산을 사용하였다. 곱하기 연산을 해줘도 되지만 shift연산이 더 빠르기도 하고 그냥 연습할 겸 사용하였다)

 

#인 경우

*과 마찬가지로 앞의 보너스에서 index를 증가시켜버렸으므로 index-1번째가 현재 점수이다.

index-1번째 값을 마이너스 값으로 만들어주면 된다.

 

그 외의 경우는 점수(숫자)인 경우이다.

문자 - '0'을 해줘서 문자를 int형으로 바꿔준다.

여기서 점수의 범위가 0에서 10이므로 10인 경우를 잘 처리해주어야 한다.

1 자릿수인 경우에는 그냥 index번째에 점수를 더해주면 되지만 10인 경우는 1이 먼저 저장되어 있으므로 기존의 값에 10을 곱해주고 0을 다시 더해줘서 10으로 만들어준다.

10이 아닌 경우라도 기존의 값이 0이기 때문에 10을 곱해주고 현재 점수를 더해줘도 상관없다.

 

 

dartResult 문자열에 대한 처리가 모두 끝났다면 arr배열에 저장되어 있는 점수들을 모두 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
#include <string>
 
using namespace std;
 
int arr[3];
 
int solution(string dartResult) {
    int answer = 0;
    
    int index = 0;
    int size = dartResult.size();
    
    for(int i=0; i<size; i++) {
        
        if(dartResult[i] == 'S') {
           index++;
        } else if (dartResult[i] == 'D') {
            arr[index] *= arr[index];
            index++;
        } else if (dartResult[i] == 'T') {
             arr[index] *= arr[index] * arr[index];
            index++;
        } else if( dartResult[i] == '*') {
            arr[index-1= (arr[index-1<< 1);
            if(index-2 >= 0) {
                arr[index-2= (arr[index-2<< 1);
            }
        } else if(dartResult[i] == '#') {
            arr[index-1= -arr[index-1];
        } else {
            arr[index] = arr[index]*10 + (dartResult[i] - '0');
        }
        
    }
    
    answer = arr[0+ arr[1+ arr[2];
    return answer;
}
Colored by Color Scripter
 

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

 

코딩테스트 연습 - [1차] 비밀지도 | 프로그래머스

비밀지도 네오는 평소 프로도가 비상금을 숨겨놓는 장소를 알려줄 비밀지도를 손에 넣었다. 그런데 이 비밀지도는 숫자로 암호화되어 있어 위치를 확인하기 위해서는 암호를 해독해야 한다. 다행히 지도 암호를 해독할 방법을 적어놓은 메모도 함께 발견했다. 지도는 한 변의 길이가 n인 정사각형 배열 형태로, 각 칸은 공백(" ) 또는벽(#") 두 종류로 이루어져 있다. 전체 지도는 두 장의 지도를 겹쳐서 얻을 수 있다. 각각 지도 1과 지도 2라고 하자. 지도 1

programmers.co.kr

이 문제는 비트 마스크를 이용해서 쉽게 풀 수 있다.

 

전체 지도는 두 장의 지도를 겹쳐서 얻을 수 있다. 각각 지도 1과 지도 2라고 하자. 지도 1 또는 지도 2 중 어느 하나라도 벽인 부분은 전체 지도에서도 벽이다. 지도 1과 지도 2에서 모두 공백인 부분은 전체 지도에서도 공백이다.

 

라는 문제의 조건에서 두 지도를 OR 연산한 결과가 전체 지도라는 것을 알 수 있다.

 

지도 1과 지도 2의 각 행을 OR연산한 후에 1이 있는 곳에는 '#' (벽)을, 0이 있는 곳에는 ' ' (공백)을 문자열에 추가하여 answer벡터에 넣어준다. 1이 있는 곳은 shift연산을 통해서 알아낼 수 있다.

 

예를 들어 첫 번째 예제에 나와있는 전체 지도의 3번째 줄은 11101이 되는데

(1<<j)  (0<=j, j < n)한 값과 (지도 1과 2의 OR 연산한 결과)를 AND 연산해줬을 때, 0이 아닌 값이 나오면 1이 있다는 뜻이다.

 

이 예제의 경우

11101 & 10000 = 10000 (0이 아니므로 벽#)

11101 & 01000 = 01000 (0이 아니므로 벽#)

11101 & 00100 = 00100 (0이 아니므로 벽#)

11101 & 00010 = 00000 (0이므로 공백)

11101 & 00001 = 00001 (0이 아니므로 벽#)

 

위와 같은 결과가 나와서 문자열은 "### #" 이 된다.

이런 식으로 모든 행에 대해서 전체 지도를 구해줄 수 있다.

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
#include <string>
#include <vector>
 
using namespace std;
 
vector<string> solution(int n, vector<int> arr1, vector<int> arr2) {
    vector<string> answer;
    
    for(int i=0; i<n; i++) {
        
        int tmp = (arr1[i] | arr2[i]);
        
        string s = "";
        
        for(int j=n-1; j>=0; j--) {
            if(tmp & (1<<j)) {
                s += "#";
            } else {
                s += " "
            }
        }
        
        answer.push_back(s);
    }
    
    return answer;
}
Colored by Color Scripter
 

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

 

코딩테스트 연습 - 실패율 | 프로그래머스

실패율 슈퍼 게임 개발자 오렐리는 큰 고민에 빠졌다. 그녀가 만든 프랜즈 오천성이 대성공을 거뒀지만, 요즘 신규 사용자의 수가 급감한 것이다. 원인은 신규 사용자와 기존 사용자 사이에 스테이지 차이가 너무 큰 것이 문제였다. 이 문제를 어떻게 할까 고민 한 그녀는 동적으로 게임 시간을 늘려서 난이도를 조절하기로 했다. 역시 슈퍼 개발자라 대부분의 로직은 쉽게 구현했지만, 실패율을 구하는 부분에서 위기에 빠지고 말았다. 오렐리를 위해 실패율을 구하는 코드를

programmers.co.kr

게임의 실패율을 구하고 실패율이 높은 스테이지 번호를 순서대로 출력하는 문제이다.

실패율이 같다면 스테이지 번호가 작은 순으로 출력한다.

 

실패율은 다음과 같은 식으로 구한다.

실패율 = 스테이지에 도달했으나 아직 클리어하지 못한 플레이어의 수 / 스테이지에 도달한 플레이어 수

 

5 [2, 1, 2, 6, 2, 4, 3, 3] [3,4,2,1,5]
4 [4,4,4,4,4] [4,1,2,3]

 

나는 먼저 주어지는 stages벡터를 정렬해주었다.

문제에서 주어진 첫 번째 예제(위의 표)를 예로 들어보면 stages를 정렬해줬을 때

1, 2, 2, 2, 3, 3, 4, 6 이 될 것이다.

그러면 스테이지 수만큼 for문을 돌면서 스테이지 번호와 현재 도전 중이 스테이지 번호가 같은 애들 사용자들에 대해서 순차적으로 처리해줄 수 있기 때문이다.

 

즉, i번째 스테이지 일 때, stages의 값이 i인 사용자들(해당 스테이지에 도전 중인 사용자, 클리어하지 못한 사용자)의 수를 스테이지에 도달한 플레이어 수로 나눠주면 된다. 그리고 현재 스테이지에 도전 중인 사용자들은 다음 스테이지에는 도달하지 못하므로 도전중인 사용자수에서 빼준다. 그리고 실패율을 해당 스테이지의 번호와 함께 벡터에 넣어준다.

 

여기서 실수할 수 있는 부분은 문제에서 스테이지에 도달한 유저가 없는 경우 해당 스테이지의 실패율은 0으로 정의한다라는 문제의 조건

을 잘 처리해주어야 한다. i번째 스테이지에 도전 중인 사용자나 클리어한 사용자가 모두 없다면 실패율은 0이 될 것이고 i번 이후의 스테이지들의 실패율도 모두 0이 될 것이다.

 

 

각 스테이지의 실패율을 모두 구했으면 정렬해주어야 한다.

실패율은 내림차순으로 그 실패율이 같다면 스테이지 번호를 오름차순으로 정렬해주기 위해서 비교 함수를 따로 구현(comp)하였다.

정렬이 끝났다면 정렬된 벡터의 두 번째 값(스테이지 번호)을 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
#include <string>
#include <vector>
#include <algorithm>
 
using namespace std;
 
bool comp(const pair<double,int> &a, const pair<doubleint> &b) {
    //실패율 먼저 비교(내림차순)
    if(a.first > b.first) {
        return true;
    } else if(a.first == b.first) {
        //실패율이 같다면 스테이지 번호를 비교(오름차순)
        if(a.second < b.second) {
            return true;
        } else {
            return false;
        }
    } else {
        return false;
    }
}
 
 
vector<int> solution(int N, vector<int> stages) {
    vector<int> answer;
    vector<pair<double,int> > failrate;
    
    sort(stages.begin(),stages.end());
    
    //도전중인 사용자 수
    int usernum = stages.size();
    int index = 0;
    
    for(int i=1; i<=N; i++) {
        
        //i번째 스테이지에 도달한 유저가 없는 경우 실패율은 0
        if(usernum == 0) {
            failrate.push_back(make_pair(0, i));
            continue;
        }
        
        
        int failcnt = 0;
        //i번 stage에 도달했지만 클리어하지 못한 사용자수
        while(stages[index] == i) {
            failcnt += 1;
            index++;
        }
 
        
        double rate = (double) failcnt/usernum;
        failrate.push_back(make_pair(rate, i));
        
        //i번째까지만 도달한 사용사들의 수를 빼준다.
        usernum -= failcnt;
    }
    
    //문제 조건에 맞게 정렬(실패율 내림차순, 스테이지번호 오름차순)
    sort(failrate.begin(),failrate.end(),comp);
    
    //정답에 실패율이 높은 스테이지의 인덱스를 추가한다.
    for(int i=0; i<N; i++) {
        answer.push_back(failrate[i].second);
    }
    
    
    return answer;
}
Colored by Color Scripter
 

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

 

코딩테스트 연습 - 오픈채팅방 | 프로그래머스

오픈채팅방 카카오톡 오픈채팅방에서는 친구가 아닌 사람들과 대화를 할 수 있는데, 본래 닉네임이 아닌 가상의 닉네임을 사용하여 채팅방에 들어갈 수 있다. 신입사원인 김크루는 카카오톡 오픈 채팅방을 개설한 사람을 위해, 다양한 사람들이 들어오고, 나가는 것을 지켜볼 수 있는 관리자창을 만들기로 했다. 채팅방에 누군가 들어오면 다음 메시지가 출력된다. [닉네임]님이 들어왔습니다. 채팅방에서 누군가 나가면 다음 메시지가 출력된다. [닉네임]님이 나갔습니다. 채팅

programmers.co.kr

이 문제는 map을 사용하여서 풀 수 있다. 각 유저 아이디를 key값으로 닉네임을 value로 map에 저장하면 된다.

나중에 닉네임을 바꾸거나 나갔다가 새로 들어올 때, 새로운 닉네임으로 적용돼야 하므로 해당 유저 아이디에 대한 닉네임을 바꿔줘야 한다. 즉, map에 이미 유저 아이디가 이미 존재하는 경우에는 새로운 닉네임(value)으로 바꿔버리면 된다.

 

 

사실 c++을 사용하다 보니까 map이 문제가 아니고 문자열 처리가 귀찮았다...

먼저 각 record에서 유저 아이디와 닉네임을 구분하기 위해서 공백을 기준으로 잘라줘야 한다.

C++에서 이 부분을 처리하려면 다른 방법도 있지만 char 배열과 strtok함수를 사용하면 된다.

strtok 함수는 csting을 include 해주면 된다.

 

 

1. 먼저 string을 char배열로 바꾼 후에

2. char 배열을 공백을 기준으로 잘라주고

3. 다시 string 2차원 배열에 넣어준다.

 

 

string 2차원 배열은 arr[100000][3]으로 선언해놨다. 100000은 문제에서 주어지는 record의 최대 길이이다.

 

arr[index][0] 은 index번째 record의 Enter/Change/Leave 중 하나의 값을 가지고

arr[index][1] 은 index번째 record의 유저 아이디

arr[index][2] 는 index번째 record의 닉네임을 가지도록 저장하였다.

 

모든 record에 대해서 공백을 기준으로 자르고 다시 새로운 string 배열에 넣어줬다면

각 record의 첫 번째 문자열이 Enter이냐 Leave이냐에 따라서 처리해주면 된다(Change일 때는 메시지가 안 뜨므로 처리해줄 필요 없다)

저장해둔 map을 이용하여 Enter인 경우에는 map[key]값과 함께 "님이 들어왔습니다"를 출력해주면 되고

Leave인 경우에는 map[key]값과 함께 "님이 나갔습니다"를 출력해주면 된다.

 

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
#include <string>
#include <vector>
#include <cstring>
#include <map>
 
using namespace std;
 
string arr[100000][3];
 
vector<string> solution(vector<string> record) {
    vector<string> answer;
    
    char str_buff[30];
    map<stringstring> m;
    
    int index = 0;
    for(string r : record) {
        //string을 char array로 바꿔준다.
        strcpy(str_buff, r.c_str());
        int cnt = 0;
        
        //공백을 기준으로 자른다.
        char *tok = strtok(str_buff, " ");
        while (tok != nullptr) {
            //다시 string 배열에 넣어준다. 
            arr[index][cnt++= string(tok);
            
            //다시 공백을 기준으로 자른다.
            tok = strtok(nullptr, " ");
        }
        
        
        //새로 들어오거나 이름을 변경한 경우에만 map에 추가 및 수정
        if(arr[index][0== "Enter" || arr[index][0== "Change") {
            if(m.find(arr[index][1]) == m.end()) {
                //map에 유저아이디가 없다면 새로 추가해준다.
                m.insert(make_pair(arr[index][1],arr[index][2]));
            } else {
                //map에 유저아이디가 이미 있으면 새로운 닉네임으로 바꿔준다.
                m[arr[index][1]] = arr[index][2];
            }
        }
        
        index++;
    }
    
    
    int size = record.size();
    for(int i=0; i<size; i++) {
        if(arr[i][0== "Enter") {
            answer.push_back(m[arr[i][1]] + "님이 들어왔습니다.");
        } else if(arr[i][0== "Leave") {
            answer.push_back(m[arr[i][1]] + "님이 나갔습니다.");
        }
    }
    
    
    
    return answer;
}
Colored by Color Scripter
 

+ Recent posts