๋ฌธ์์ด
1. KMP ์๊ณ ๋ฆฌ์ฆ
๋ฌธ์์ด ๊ฒ์์ ๋น ๋ฅด๊ฒ ๊ตฌํ๊ธฐ ์ํ ์๊ณ ๋ฆฌ์ฆ์ด๋ค.
1) ๊ธฐ๋ณธ ์๋ฆฌ
์ ๋์ฌ(prefix)์ ์ ๋ฏธ์ฌ(suffix)๋ฅผ ์ด์ฉํด pi[i] ๋ฐฐ์ด์ ๊ตฌํ ๋ค ํ์ฉํ๋ค. pi[i] ๋ฐฐ์ด์ 0~i๊น์ง์ ๋ถ๋ถ ๋ฌธ์์ด์์ prefix=suffix๊ฐ ๋ ์ ์๋ ๋ถ๋ถ ๋ฌธ์์ด ์ค ๊ฐ์ฅ ๊ธด ๊ฒ์ ๊ธธ์ด์ด๋ค. ์ด๋, prefix๊ฐ 0~i๊น์ง์ ๋ถ๋ถ ๋ฌธ์์ด๊ณผ ๊ฐ์ผ๋ฉด ์ ๋๋ค.
์๋ฅผ ๋ค์ด ๋ฌธ์์ด ABAABAB๋ก pi[i] ๋ฐฐ์ด์ ๊ตฌํด๋ณด์.
i | ๋ถ๋ถ ๋ฌธ์์ด (0~i) | pi[i] |
0 | A | 0 |
1 | AB | 0 |
2 | ABA | 1 |
3 | ABAA | 1 |
4 | ABAAB | 2 |
5 | ABAABA | 3 |
6 | ABAABAB | 2 |
๊ทธ๋ ๋ค๋ฉด ์ด pi[i] ๋ฐฐ์ด์ ์ด๋ป๊ฒ ํ์ฉํ์ฌ ๋ฌธ์์ด ๊ฒ์์ ํจ์จ์ ์ผ๋ก ํ ๊ฒ์ธ๊ฐ?
๋ฌธ์์ด ABCDABCDABEE์์ ๋ฌธ์์ด ABCDABE๋ฅผ ์ฐพ๋ ์ํฉ์ ๊ฐ์ ํด๋ณด์. ์๋์ ๊ฐ์ ์ํฉ์์ ๋ฌธ์์ด ๋งค์น์ ์คํจํ์ ๋, ๊ธฐ์กด ๋ฌธ์์ด ๊ฒ์์ ์ธ๋ฑ์ค 1๋ก ์ด๋ํด ๊ฒ์์ ์งํํ๋ค.
์ธ๋ฑ์ค | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
ํ ์คํธ | A | B | C | D | A | B | C | D | A | B | E | E |
ํจํด | A | B | C | D | A | B | E |
๊ทธ๋ฌ๋, pi[i] ๋ฐฐ์ด์ ์ฌ์ฉํ๋ค๋ฉด ํ์ฌ ๋ ๋ฌธ์์ด์ด ์ผ์นํ๋ ๋ถ๋ถ ๋ฌธ์์ด์ ABCDAB(0~5)์ด๊ณ pi[5]=2์ด๋ฏ๋ก ์ด๋ฏธ ๋ฌธ์์ด AB๊ฐ ์ผ์นํ๋ค๋ ๊ฒ์ ์ ์ ์์ด ์ธ๋ฑ์ค 1์ด ์๋ ์ธ๋ฑ์ค 4๋ก ๋์ด๊ฐ ๊ฒ์์ ์งํํ ์ ์๋ค.
2) ๋ฌธ์
https://www.acmicpc.net/problem/1786
#include <iostream>
#include <vector>
#include <string>
using namespace std;
vector<int> kmp(string T, string P)
{
vector<int> answer;
int n = T.size(), m = P.size();
// pi[i] ๋ฐฐ์ด ๊ตฌํ
vector<int> pi(m, 0);
for (int i = 1, j = 0; i < m; i++) {
while (j > 0 && P[i] != P[j])
j = pi[j - 1];
if (P[i] == P[j])
pi[i] = ++j;
}
// kmp ํจ์ ๊ตฌํ
for (int i = 0, j = 0; i < n; i++) {
while (j > 0 && T[i] != P[j])
j = pi[j - 1];
if (T[i] == P[j]) {
if (j == m - 1) {
answer.push_back(i - m + 2);
j = pi[j];
}
else {
j++;
}
}
}
return answer;
}
int main()
{
string T, P;
getline(cin, T);
getline(cin, P);
vector<int> answer = kmp(T, P);
cout << answer.size() << "\n";
for (auto i : answer)
cout << i << "\n";
}
2. ๋ผ๋น ์นดํ ์๊ณ ๋ฆฌ์ฆ(Rabin-Karp Algorithm)
๋ฌธ์์ด ๊ฒ์์ ๋น ๋ฅด๊ฒ ๊ตฌํ๊ธฐ ์ํ ์๊ณ ๋ฆฌ์ฆ์ด๋ค.
1) ๊ธฐ๋ณธ ์๋ฆฌ
ํด์ฑ์ ๊ธฐ๋ฐํ๋ค. ๋ฌธ์์ด ๋ด ๋ชจ๋ ์์น์์ ๋น๊ตํ๋๋ฐ, ๋จผ์ ํด๋น ์์น์ ํด์๊ฐ์ ๋น๊ตํ๊ณ ๊ฐ๋ค๋ฉด ๊ทธ์ ์ผ ๋จ์ ๋น๊ต๋ฅผ ํ๋ค. ํด์ ํจ์๋ฅผ ์ ๊ตฌํํด์ ์ถฉ๋์ด ์ ์ ํด์ ํจ์๋ฅผ ๋ง๋ ๋ค๋ฉด ์ ์ฉํ ์๊ณ ๋ฆฌ์ฆ์ด ๋ ๊ฒ์ด๋ค.
๊ฐ์ฅ ๋ง์ด ์ฌ์ฉ๋๋ ํด์ ํจ์๋ Rabin fingerprint๋ก ์๋์ ๊ฐ์ ํํ๋ฅผ ๊ฐ๊ณ ์๋ค.
- T[i]: ํด๋น ๋ฌธ์์ ์์คํค์ฝ๋๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.
H(i) = T[i] *2^(M-1)+ T[i+1] * 2^(M-2)+ ... + T[i+M-2]*2^(1) + T[i+M-1]*2^(0)
H(i) = 2 * (H(i-1) - T[i - 1] * 2^(M-1)) + T[i+M-1] (์ ํ์)
2) ๋ฌธ์
https://www.acmicpc.net/problem/3033
#include <iostream>
#include <vector>
#include <string>
using namespace std;
const int HASH_SIZE = 100003;
int Mod(long long x)
{
if (x >= 0)
return x % HASH_SIZE;
return ((-x / HASH_SIZE + 1) * HASH_SIZE + x) % HASH_SIZE;
}
int main()
{
int L;
string str;
cin >> L >> str;
int start = 0, end = L;
while (start + 1 < end) {
vector <int> hash[HASH_SIZE];
int mid = (start + end) / 2, h = 0, po = 1;
bool found = false;
// ํด์๊ฐ(h) ๋ง๋ค๊ธฐ
for (int i = 0; i <= L - mid; i++) {
if (i == 0) {
for (int j = 0; j < mid; j++) {
h = Mod(h + 1LL * str[mid - j - 1] * po);
if (j < mid - 1)
po = Mod(po * 2);
}
}
else
h = Mod(2 * (h - 1LL * str[i - 1] * po) + str[i + mid - 1]);
if (!hash[h].empty()) {
// ํด์ ์ถฉ๋์ผ ๋ str์ [i, i+m-1]์ [p, p+mid-1]์ ๋ฌธ์์ด์ด ๊ฐ์์ง ๋น๊ต
for (int p : hash[h]) {
for (int j = 0; j < mid; j++) {
if (str[i + j] != str[p + j])
break;
if (j == mid - 1)
found = true;
}
}
}
if (!found)
hash[h].push_back(i);
}
if (found)
start = mid; // ์ฐพ์์ผ๋ฉด ๋ ๋์ ๊ธธ์ด ํ์
else
end = mid; // ์์ผ๋ฉด ๋ ๋ฎ์ ๊ธธ์ด ํ์
}
cout << start;
}
3. ๋งจ๋ฒ-๋ง์ด์ด์ค ์๊ณ ๋ฆฌ์ฆ
์ ๋ฏธ์ฌ ๋ฐฐ์ด์ ๋น ๋ฅด๊ฒ ๊ตฌํ๊ธฐ ์ํ ์๊ณ ๋ฆฌ์ฆ์ด๋ค.
1) ๊ธฐ๋ณธ ์๋ฆฌ
์ ๋ฏธ์ฌ ๋ฐฐ์ด(Suffix Array)์ ์ด๋ค ๋ฌธ์์ด์ ๋ชจ๋ ์ ๋ฏธ์ฌ๋ฅผ ์ฌ์ ์์ผ๋ก ์ ๋ ฌํด ๋ ๋ฐฐ์ด์ด๋ค.
์๋ฅผ ๋ค์ด ๋ฌธ์์ด banana๋ก sa[i] ๋ฐฐ์ด์ ๊ตฌํด๋ณด์. ์ ๋ฏธ์ฌ๋ a, na, ana, nana, anana, banana๊ฐ ์๊ณ , ๊ทธ๊ฒ๋ค์ ์ฌ์ ์์ผ๋ก ์ ๋ ฌํ๋ฉด ๋ค์๊ณผ ๊ฐ๋ค. ์ด๋, sa[i] ๋ฐฐ์ด์๋ ์ ๋ฏธ์ฌ ๋ฌธ์์ด์ ์ ์ฅํ ์ ์์ผ๋ ํด๋น ์ ๋ฏธ์ฌ์ ์ฒซ ๋ฒ์งธ ์ธ๋ฑ์ค๋ฅผ ์ ์ฅํ๋ค.
i | sa[i] | S[sa[i]] |
0 | 5 | a |
1 | 3 | ana |
2 | 1 | anana |
3 | 0 | banana |
4 | 4 | na |
5 | 2 | nana |
์ฌ๊ธฐ์ ์ ๋ฏธ์ฌ ๋ฐฐ์ด์ ๋ง๋ค๊ธฐ ์ํ ์ฌ๋ฌ ์๊ณ ๋ฆฌ์ฆ์ด ์์ง๋ง, ๊ทธ์ค ๋งจ๋ฒ-๋ง์ด์ด์ค ์๊ณ ๋ฆฌ์ฆ์ ํ์ฉํ ๊ฒ์ด๋ค. ์๋ฆฌ๋ ์ ๋ฏธ์ฌ๋ฅผ ์ฒซ ํ ๊ธ์, ์ฒซ ๋ ๊ธ์, ์ฒซ ๋ค ๊ธ์, ์ฒซ ์ฌ๋ ๊ธ์... ์ฒซ d*2 ๊ธ์ ์ฉ ๋น๊ตํด๊ฐ๋ฉฐ ์ ๋ ฌํ๋ ๊ฒ์ด๋ค.
์ฒซ ํ ๊ธ์(d=0) ๊ธฐ์ค์ผ ๋, ๊ทธ๋ฃน 3๊ฐ๋ก ๋๋๋ฉฐ ๊ทธ๋ฃน 1๊ณผ ๊ทธ๋ฃน 3์ ์ถ๊ฐ ๊ณ์ฐ์ด ํ์ํ๋ค.
- ๊ทธ๋ฃน 1: anana, ana, a
- ๊ทธ๋ฃน 2: banana
- ๊ทธ๋ฃน 3: nana, na
์ฒซ ๋ ๊ธ์(d=1) ๊ธฐ์ค์ผ ๋, ๊ทธ๋ฃน 4๊ฐ๋ก ๋๋๋ฉฐ ๊ทธ๋ฃน 2์ ๊ทธ๋ฃน 4์ ์ถ๊ฐ ๊ณ์ฐ์ด ํ์ํ๋ค.
- ๊ทธ๋ฃน 1: a
- ๊ทธ๋ฃน 2: anana, ana
- ๊ทธ๋ฃน 3: banana
- ๊ทธ๋ฃน 4: nana, na
๋ง์ง๋ง์ผ๋ก ์ฒซ ๋ค ๊ธ์(d=2) ๊ธฐ์ค์ผ ๋, ๋๋์ด ๋ชจ๋ ์ ๋ฏธ์ฌ๊ฐ ๊ฐ๊ฐ์ ๊ทธ๋ฃน์ผ๋ก ๋๋์๋ค. (๊ทธ๋ฃน์ = ์ ๋ฏธ์ฌ์)
- ๊ทธ๋ฃน 1: a
- ๊ทธ๋ฃน 2: ana
- ๊ทธ๋ฃน 3: anana
- ๊ทธ๋ฃน 4: banana
- ๊ทธ๋ฃน 5: na
- ๊ทธ๋ฃน 6: nana
ํ ๋ฒ ๋ค๋ฅธ ๊ทธ๋ฃน์ ์ํ ์ ๋ฏธ์ฌ๋ค์ ๋ค์ ๊ฐ์ ๊ทธ๋ฃน์ ์ํ ์ ์๊ณ ๊ฐ์ ๊ทธ๋ฃน์ ์ํ๋ ค๋ฉด ์ฒซ d*2 ๊ธ์๊ฐ ๊ฐ์์ผ ํ๋ค.
์ฒซ ํ ๊ธ์(d=0) ๊ธฐ์ค์ผ ๋๋ก ์์ํด ์ฒ์ ๊ทธ๋ฃน์ ๋๋ ํ ๊ทธ๋ค์ ๋ฃจํ๋ถํฐ๋ ๊ฐ ์ ๋ฏธ์ฌ๊ฐ ์ด์ ๋ฃจํ์์ ๋ช ๋ฒ์งธ ๊ทธ๋ฃน์ ์ํ๋์ง ์๋ค๋ฉด 1์ฐจ์ ์ธ ๋น๊ต๊ฐ ๊ฐ๋ฅํ๋ค.
- ๋ ์ ๋ฏธ์ฌ๊ฐ ์ด์ ๋ฃจํ์์ ๊ทธ๋ฃน ๋ฒํธ๊ฐ ๋ค๋ฅด๋ค๋ฉด, ๊ทธ๋ฃน ๋ฒํธ๊ฐ ํด์๋ก ์ฌ์ ์์ผ๋ก ๋ค์ ์๋ ๊ฒ์ด๋ค.
- ๋ ์ ๋ฏธ์ฌ๊ฐ ์ด์ ๋ฃจํ์์ ๊ทธ๋ฃน ๋ฒํธ๊ฐ ๊ฐ๋ค๋ฉด, ๊ทธ์ ์ผ d*2 ๊ธ์๋ถํฐ ๋น๊ตํ๋ค. ์ด๋, d*2 ๊ธ์๋ถํฐ ๋๊น์ง๋ ๋ฌธ์์ด์ ์ ๋ฏธ์ฌ ์ค ํ๋๊ฐ ๋๋ฏ๋ก ๋ค์ ๊ทธ๋ฃน ๋ฒํธ๋ก ๋น๊ตํ๋ค.
2) ๋ฌธ์
https://www.acmicpc.net/problem/9248
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int sa[500000], lcp[500000];
int g[500000], tg[500000];
string s;
int n, t;
bool compare(int a, int b)
{
if (g[a] == g[b])
return g[a + t] < g[b + t];
return g[a] < g[b];
}
void SuffixArray()
{
t = 1, g[n] = -1;
for (int i = 0; i < n; i++) {
sa[i] = i;
g[i] = s[i] - 'a'; // ์ฒซ ๋ฒ์งธ ๋ฃจํ๋ ์ ์๋ฆฌ ๋ฌธ์๋ก ๋น๊ต
}
while (t <= n) {
sort(sa, sa + n, compare);
// ์์์๋ถํฐ ๊ฐ ์ ๋ฏธ์ฌ๊ฐ ๋ค๋ฅธ ๊ทธ๋ฃน์ ์ํ ๋๋ง๋ค ๊ทธ๋ฃน ๋ฒํธ๋ฅผ ์ฆ๊ฐ์ํจ๋ค.
for (int i = 1; i < n; i++)
tg[sa[i]] = tg[sa[i - 1]] + (compare(sa[i - 1], sa[i]) ? 1 : 0);
t *= 2;
for (int i = 0; i < n; i++)
g[i] = tg[i];
}
}
void LCP()
{
int tmp[500000]{};
for (int i = 0; i < n; i++)
tmp[sa[i]] = i;
int len = 0;
for (int i = 0; i < n; i++) {
if (tmp[i]) {
int j = sa[tmp[i] - 1];
while (s[j + len] == s[i + len])
len++;
lcp[tmp[i]] = len;
if (len)
len--;
}
}
}
int main()
{
cin >> s;
n = s.length();
SuffixArray();
LCP();
for (int i = 0; i < n; i++)
cout << sa[i] + 1 << " ";
cout << "\nx ";
for (int i = 1; i < n; i++)
cout << lcp[i] << " ";
}
4. ์ํธ ์ฝ๋ผ์ ์๊ณ ๋ฆฌ์ฆ(Aho-Corasick Algorithm)
- ์ผ๋๋ค ํจํด ๋งค์นญ ์๊ณ ๋ฆฌ์ฆ์ผ๋ก ๋ฌธ์์ด ํ๋ ์์ ์ฌ๋ฌ ๊ฐ๊ฐ์ ๋ฌธ์์ด์ด ์กด์ฌํ๋์ง๋ฅผ ํ๋ณํ๋ค.
- KMP ์๊ณ ๋ฆฌ์ฆ์ ํ์ฅ์ด๋ค.
1) ๊ธฐ๋ณธ ์๋ฆฌ
๊ทธ๋ฆผ์ ๋ณด๋ฉด ์๊ฒ ์ง๋ง, ์ผ์ข ์ ํธ๋ผ์ด์ด๋ค.
- ๋ฌธ์์ด S ๋ด ๋ฌธ์ c๋ฅผ ํ๋์ฉ ์ฝ์ด๊ฐ๋ฉฐ ์งํํ๋๋ฐ, ์์ state๋ ์ ์ผ ์ผ์ชฝ ๋ ธ๋์ด๋ค.
- ๋ค์ state๋ go ํจ์๋ก ์ ์ํ๋ฉฐ ์ด ํจ์๋ฅผ ํตํด ์ด๋ํ๋ค.
- output ๋ ธ๋(๋นจ๊ฐ ๋ ธ๋)์ ๋์ฐฉํ๋ค๋ฉด, ๋ ธ๋์ ์ ํ์๋ ๋ฌธ์์ด์ด ๋ฌธ์์ด S์ ๋ฑ์ฅํ๋ค๋ ๋ป์ด๋ค.
- ๋ง์ฝ ๊ฐ ๊ณณ์ด ์์ด์ง๋ฉด, ๋จ์ํ ์์ ์ง์ ์ด ์๋๋ผ fail ํจ์๋ฅผ ํตํด ๋ค๋ฅธ ์ด๋ค ๊ณณ์ผ๋ก ๋ณด๋ธ๋ค.
์ ๋ฆฌํ์๋ฉด ๋ค์ state๋ก ์ด๋ํ๋ ๋ฉ์ปค๋์ฆ์ ๋ค์๊ณผ ๊ฐ๋ค.
1. ํ์ฌ state x, ์ธํ c์ ๋ํด go(x, c)๊ฐ ์์ผ๋ฉด ํด๋น ๋ ธ๋๋ก ์ด๋ํ๋ค.
2. ๋ง์ฝ ์๋ค๋ฉด, fail(x)๋ก ์ด๋ํ ํ 1๋ก ๋์๊ฐ๋ค. ๋จ, ์ด๋ฏธ ๋ฃจํธ๋ฉด ์๋ฌด๊ฒ๋ ํ์ง ์๋๋ค.
์ฌ๊ธฐ์ fail ํจ์๋ฅผ ์ผ๋ง๋ ํจ์จ์ ์ด๊ฒ ๊ตฌํํ๋๋๊ฐ ํฌ์ธํธ์ด๋ค.
- fail ํจ์๋ ๋ฐ๋์ ์์ ๋ณด๋ค ๊น์ด๊ฐ ์์ ๋ ธ๋๋ฅผ ๊ฐ๋ฆฌํจ๋ค. (๋ฃจํ ๋ ธ๋ ์ ์ธ)
- fail(x)=y์ด๋ฉด, y๋ x์ ์ ๋ฏธ์ฌ ์ค ๊ฐ์ฅ ๊ธด ์ ๋ฏธ์ฌ์ด๋ค. (x ์์ ์ ์ธ)
2) ๋ฌธ์
https://www.acmicpc.net/problem/9250
#include <iostream>
#include <queue>
#include <string>
#include <algorithm>
using namespace std;
struct Trie {
Trie* go[26]; // ํ์ฌ ๋
ธ๋์์ ํด๋น ๋ฌธ์๋ฅผ ๋ฐ์ผ๋ฉด ๊ฐ๋ ๋
ธ๋
Trie* fail; // ํ์ฌ ๋
ธ๋์์ ํด๋น ๋ฌธ์์ go ๋ชฉ์ ์ง๊ฐ ์์ ๋ ๊ฐ๋ ๋
ธ๋
bool output; // ํ์ฌ ๋
ธ๋์ ๋๋ฌํ๋ฉด ์ฐพ๋ ๋ฌธ์์ด ์งํฉ ์๋์ง ํ๋จ
Trie()
{
fill(go, go + 26, nullptr);
output = false;
}
~Trie()
{
for (int i = 0; i < 26; i++)
if (go[i])
delete go[i];
}
void insert(string key)
{
if (key == "\0") {
output = true;
return;
}
int next = key[0] - 'a';
if (!go[next])
go[next] = new Trie;
// ๋งจ ์ ๋ฌธ์๋ ์ ์ธํ๊ณ ์ ๋ฌํ๋ค.
go[next]->insert(key.substr(1, key.size() - 1));
}
};
int main()
{
int N, M;
string str;
Trie* root = new Trie;
cin >> N;
for (int i = 0; i < N; i++) {
cin >> str;
// ํธ๋ผ์ด์ S์ ์์๋ค์ ๋ชจ๋ ์ง์ด๋ฃ๋๋ค.
root->insert(str);
}
// ํธ๋ผ์ด ๋
ธ๋๋ฅผ ๋ฐฉ๋ฌธํ๋ฉฐ fail ํจ์๋ฅผ ๋ง๋ ๋ค.
queue<Trie*> q;
root->fail = root;
q.push(root);
while (!q.empty()) {
Trie* current = q.front();
q.pop();
// 26๊ฐ์ input ๊ฐ๊ฐ์ ๋ํด ์ฒ๋ฆฌํ๋ค.
for (int i = 0; i < 26; i++) {
Trie* next = current->go[i];
if (!next)
continue;
// ๋ฃจํธ์ fail์ ๋ฃจํธ๋ค.
if (current == root)
next->fail = root;
else {
Trie* dest = current->fail;
// fail์ ์ฐธ์กฐํ ๊ฐ์ฅ ๊ฐ๊น์ด ์กฐ์์ ์ฐพ์๊ฐ๋ค.
while (dest != root && !dest->go[i])
dest = dest->fail;
// fail(px) = go(fail(p), x)
if (dest->go[i])
dest = dest->go[i];
next->fail = dest;
}
// fail(x) = y์ผ ๋, output(y) ⊂ output(x)
if (next->fail->output)
next->output = true;
// ํ์ ๋ค์ ๋
ธ๋ push
q.push(next);
}
}
cin >> M;
for (int i = 0; i < M; i++) {
cin >> str;
Trie* current = root;
bool result = false;
for (int j = 0; str[j]; j++) {
int next = str[j] - 'a';
// ํ์ฌ ๋
ธ๋์์ ๊ฐ ์ ์์ผ๋ฉด fail์ ๊ณ์ ๋ฐ๋ผ๊ฐ
while (current != root && !current->go[next])
current = current->fail;
// go ํจ์๊ฐ ์กด์ฌํ๋ฉด ์ด๋. ๋ฃจํธ๋ฉด ์ด๊ฒ false์ผ ์๋ ์๋ค
if (current->go[next])
current = current->go[next];
// ํ์ฌ ๋
ธ๋์ output์ด ์์ผ๋ฉด ์ฐพ์ ๊ฒ์ด๋ค.
if (current->output) {
result = true;
break;
}
}
cout << (result ? "YES" : "NO") << endl;
}
}
์ฐธ๊ณ
https://bowbowbow.tistory.com/6