어떻게 풀까?
위 문제를 해결하기 위해서는 확률식을 세워보는 것이 중요합니다.
우선 원문의 i번째 글자를 Q[i]라고 하고, 현재 주어진 문장의 i번째 글자를 R[i]라고 합시다.
그러면 이전의 글자에서 다음 글자로의 확률은 T(Q[i - 1], Q[j]) 으로 나타나고,
원문에서 현재 주어진 문장으로 번역될 확률은 M(Q[i], R[i])로 번역될 수 있습니다.
한편, 맨 처음의 단어는 예외적으로 T를 이용하지 않고, B(Q[0])을 이용해야 합니다.
여기서, 약간의 테크닉을 활용해서 모든 단어의 인덱스가 1부터 시작하도록 하고,
0번 인덱스는 무조건 0번 단어라고 가정해 봅시다.
그러면, B(Q[0])는 대신 B(Q[1])을 이용해서 표기할 수 있습니다.
이는 동시에 T(Q[0], Q[1])으로 나타낼 수 있습니다!
이는 B의 배열은 T의 0행에 넣도록 해서, T 배열 행의 크기를 6으로 늘리는 방법을 이용하여 표기하도록 합시다.
아래 그림을 통해 확인해 봅시다.
그럼 이제 현재 위치가 i일 때,
현재 글자가 맞을 확률은 다음과 같이 구할 수 있습니다.
T(Q[i-1], Q[i]) * M(Q[i], R[i])
이를 이용하면, 주어진 문장에서 원문이 될 확률은
입니다.
위 공식에서 가장 중요한 부분은, 바로, 이전 글자에 따라서 현재 글자의 확률이 달라지는 부분입니다!
그림을 통해서 확인하겠습니다.
그림은 M("I", "am)부터 M("buy", am) 까지의 확률을 나타낸 것 입니다.
하지만, 실제로 Q[i]가 맞을 확률은 앞글자가 무엇이냐에 따라서 곱해여야 하는 T가 달라집니다.
따라서, 문제를 풀 때, 이전의 글자가 어떤 글자였는지 넘겨주는 것이 필요합니다.
이를 이용하면,
recognize(s, p) = Q(s-1)이 p라는 글자일 때, Q(s),Q(s+1), ... 을 적절히 선택해서 만들 수 있는 최대 확률을 반환한다고 합시다.
m은 처음에 주어진 나타날 수 있는 글자들의 집합을 의미할 때 아래와 같은 식을 얻을 수 있습니다.
이를 이용해서 전체 코드를 만들어 봅시다!
코드
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 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | #pragma once #include<cstdio> #include<cstring> char str[501][11], qStr[100][11]; int sIdx[501], temp[501], qIdx[101], m, n, q; double T[501][501]; double M[501][501]; double cache[501][101]; int choice[501][101], res[100], rLen; void mergeSort(int left, int m, int right) { int l = left, r = m + 1, k = left; while (l <= m && r <= right) { if (strcmp(str[sIdx[l]], str[sIdx[r]]) < 0) { temp[k++] = sIdx[l++]; } else { temp[k++] = sIdx[r++]; } } while (l <= m) temp[k++] = sIdx[l++]; while (r <= right) temp[k++] = sIdx[r++]; for (int i = left; i <= right; i++) { sIdx[i] = temp[i]; } } void merge(int left, int right) { if (left < right) { int m = (left + right) / 2; merge(left, m); merge(m + 1, right); mergeSort(left, m, right); } } int find(const char* s) { int l = 1, r = m; while (l <= r) { int m = (l + r) / 2; int res = strcmp(str[sIdx[m]], s); if (res == 0) { return sIdx[m]; } else if (res < 0) { l = m + 1; } else { r = m - 1; } } return -1; } double ocr(int prev, int now) { if (now > n) { return 1.0; } double &ret = cache[prev][now]; if (ret > -0.5) return ret; ret = 0; double temp1, tP; for (int i = 1; i <= m; i++) { double p = M[i][qIdx[now]]; temp1 = T[prev][i]; tP = p * temp1 * ocr(i, now + 1); if (ret < tP) { ret = tP; choice[prev][now] = i; } } return ret; } void print(int prev, int now) { res[rLen++] = choice[prev][now]; if (now == n) { return; } print(choice[prev][now], now + 1); } int OCR() { scanf("%d %d\n", &m, &q); for (int i = 1; i <= m; i++) { scanf("%s \n", str[i]); } for (int i = 0; i <= m; i++) { for (int j = 1; j <= m; j++) { scanf("%lf \n", &T[i][j]); } } for (int i = 1; i <= m; i++) { for (int j = 1; j <= m; j++) { scanf("%lf \n", &M[i][j]); } } for (int i = 1; i <= m; i++) { sIdx[i] = i; } merge(1, m); while (q--) { scanf("%d\n", &n); for (int i = 1; i <= n; i++) { scanf("%s \n", qStr[i]); } for (int i = 1; i <= n; i++) { qIdx[i] = find(qStr[i]); } for (int i = 0; i <= m; i++) for (int j = 0; j <= n; j++) { choice[i][j] = -1; cache[i][j] = -1; } ocr(0, 1); rLen = 0; print(0, 1); for (int i = 0; i < rLen; i++) printf("%s ", str[res[i]]); printf("\n"); } return 0; } | cs |
'공부 > 알고리즘 문제풀이' 카테고리의 다른 글
[SW Expert Academy] 장훈이의 선반 (0) | 2018.06.21 |
---|---|
[SWExpertAcademy] Code Battle (18.8.12 수정) (2) | 2018.06.20 |
[알고스팟] 여행 짐 싸기 (2) | 2018.06.17 |
[알고스팟] 두니발 박사의 탈옥 (0) | 2018.06.17 |
[알고스팟] 폴리오미노 (0) | 2018.06.17 |