어떻게 접근할까?
위 문제는 '곱셈 연산'으로 접근이 가능합니다. 아래 그림을 통해 알아보겠습니다.
곱셈을 하면 위와 같이 결과가 나오게 됩니다.
현재 포옹을 하지 않는 조건은,
1. 남자 팬과 남자 가수가 만나서 악수를 하는 경우
2. 가수와 팬이 만나지 않아서 악수를 하지 않는 경우
두 가지 입니다.
따라서, 악수를 하는 경우는 남자 팬과 남자 가수가 만나는 경우이므로, M이면 1이라고 생각하고, F인 경우는 0 으로 설정합니다.
이렇게 하면 악수를 하는 경우는 1과 1이 곱해져 1의 결과를 가지게 되고, 나머지의 경우는 악수를 하지 않는 0의 결과를 가집니다.
따라서 아주 큰 수의 곱셈을 시행하고,
가수가 모두 들어오기 시작하는 가수의 수 - 1 자리수 부터 팬의 수 까지 0의 개수를 확인하면 그것이 바로 답이 됩니다.
그리고 주의해야 할 것이 있습니다. 바로 곱하기를 한 경우를 보면, 가수의 맨 앞의 사람이 팬의 맨 앞의 사람부터 만나는 것을 볼 수 있습니다.
문제의 그림에 따르면 가수 맨 앞의 사람은 팬의 맨 뒤의 사람부터 만나서 악수를 시작합니다.
따라서, 팬의 수를 거꾸로 뒤집고 곱하기를 해야한다는 것 입니다.
이제 곱하기를 구현하면 되는데, 보통의 곱하기는 O(n^2)의 시간복잡도를 가집니다.
아주 큰 수의 곱하기를 O(n^log 3)의 시간 복잡도를 가지도록 풀 수 있게 해주는 알고리즘이 있습니다.
이름은 '카라츠바 알고리즘' 입니다.
분할 정복을 통해서 곱셈을 자리수에 따라서 반씩 나누어서 더하면서 진행하는 방식입니다.
카리츠바 알고리즘은 다른 곳에 포스팅을 했으니 필요하면 보도록 합시다!
이렇게 카라츠바 알고리즘을 통해서 곱셈의 결과를 얻게 된다면, 위에서 말했던 자리수 범위에서 0이 몇 개인지 출력하면 됩니다!
왜냐하면, 1이 된다는 것은 악수를 하는 사람이 있다는 것을 의미하기 때문입니다! (M이 1이고 M과 M이 곱해져야만 1이 나옵니다.)
코드
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 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | #pragma once #include<cstdio> #include<cstring> #include<iostream> #include<vector> using namespace std; char groups[200001]; char fans[200001]; int len[2]; typedef vector<int> VEC; vector<int> g, f; int min(int i1, int i2) { return i1 < i2 ? i1 : i2; } int max(int i1, int i2) { return i1 > i2 ? i1 : i2; } VEC multi(VEC& A, VEC& B) { int s1 = A.size(); int s2 = B.size(); VEC res; res.resize(s1 + s2 - 1, 0); for (int i = 0; i < s1; i++) { for (int j = 0; j < s2; j++) { res[i + j] += A[i] * B[j]; } } return res; } void add(vector<int>& vec1, vector<int>& vec2, int k) { int s = max(vec2.size() + k, vec1.size()); vec1.resize(s, 0); vec2.resize(s, 0); for (int i = 0; i < s - k; i++) { vec1[i + k] += vec2[i]; } } void sub(vector<int>& vec1, vector<int>& vec2) { int s = max(vec1.size(),vec2.size()); vec1.resize(s, 0); vec2.resize(s, 0); for (int i = 0; i < s; i++) vec1[i] -= vec2[i]; } VEC Karatsuba(VEC& A, VEC& B) { if (A.size() < 50) { VEC res; res = multi(A,B); return res; } int half = A.size() / 2; if (A.empty() || B.empty()) return VEC(); VEC a1 = VEC(A.begin(), A.begin() + half); VEC a0 = VEC(A.begin() + half, A.end()); VEC b1 = VEC(B.begin(), B.begin() + min(half, B.size())); VEC b0 = VEC(B.begin() + min(half, B.size()), B.end()); VEC z0, z1, z2; z0 = Karatsuba(a0, b0); z2 = Karatsuba(a1, b1); add(a0, a1, 0); add(b0, b1, 0); z1 = Karatsuba(a0, b0); sub(z1, z0); sub(z1, z2); VEC res; add(res, z2, 0); add(res, z1, half); add(res, z0, half + half); return res; } int FANMEETING() { int t; scanf("%d\n", &t); while (t--) { scanf("%s\n", groups); scanf("%s\n", fans); len[0] = strlen(groups); len[1] = strlen(fans); f.resize(len[1], 0); g.resize(len[0], 0); for (int i = 0; i < len[0]; i++) g[i] = groups[len[0] - i - 1] == 'M'; for (int i = 0; i < len[1]; i++) f[i] = fans[i] == 'M'; VEC res = Karatsuba(f, g); int cnt = 0; for (int i = len[0] - 1; i < len[1]; i++) { cnt += res[i] == 0; } printf("%d\n", cnt); } return 0; } | cs |
'공부 > 알고리즘 문제풀이' 카테고리의 다른 글
[알고스팟] 와일드카드 (0) | 2018.06.12 |
---|---|
[알고스팟] 외발 뛰기 (0) | 2018.06.12 |
[알고스팟] 울타리 잘라내기 (0) | 2018.06.11 |
[알고스팟] 쿼드 트리 뒤집기 (0) | 2018.06.11 |
[알고스팟] CLOCKSYNC (0) | 2018.06.07 |