문제 링크
https://www.acmicpc.net/problem/3033
3033번: 가장 긴 문자열
첫째 줄에 L이 주어진다. (1 ≤ L ≤ 200,000) 다음 줄에는 길이가 L이면서 알파벳 소문자로 이루어진 문자열이 주어진다.
www.acmicpc.net
풀이 1. Hashing, Rabin-Karp algorithm, Parametric search
- 길이 $l$ 인 부분 문자열이 전체 문자열내에서 두번 이상 등장한다고 하자
- 그 부분 문자열의 부분 문자열들(길이 $1, 2, ..., l$) 또한 전체 문자열내에서 두번 이상 등장한다
- 따라서 결정문제: 길이 $l$ 부분문자열이 2번이상 등장하는가? 에 대한 답의 분포는 이분적이다($T, ..., T, F, F, ..., F$)
- 각 결정문제를 해결할 때에는 라빈카프 알고리즘의 롤링해쉬함수를 이용한다

코드 1.
- 시간복잡도: $O(LlogL)$
- Parametric search: $O(logL)$
- 결정문제: $O(L)$
#include <bits/stdc++.h> using namespace std; #define all(v) (v).begin(), (v).end() #define rall(v) (v).rbegin(), (v).rend() int main() { ios_base::sync_with_stdio(false); cin.tie(NULL); int L; cin >> L; string s; cin >> s; long long MOD = 1e5 + 3; long long MUL = 2; auto check = [&](int l) { vector<vector<int> > hashTable(MOD); long long h = 0; long long MUL_POW_L = 1; for (int i = 0; i < l; ++i) { h *= MUL; h += s[i] - 'a' + 1; h %= MOD; MUL_POW_L *= MUL; MUL_POW_L %= MOD; } hashTable[h].push_back(0); for (int i = 1; i + l - 1 < L; ++i) { h *= MUL; h %= MOD; h -= (1LL * (s[i - 1] - 'a' + 1) * MUL_POW_L) % MOD; h += s[i + l - 1] - 'a' + 1; h %= MOD; if (h < 0) h += MOD; for (auto idx: hashTable[h]) { if (s.compare(i, l, s, idx, l) == 0) return true; } hashTable[h].push_back(i); } return false; }; int lo = 0; int hi = L; while (lo + 1 < hi) { int mid = lo + hi >> 1; if (check(mid)) lo = mid; else hi = mid; } cout << lo << '\n'; }
풀이 2. Suffix array and LCP array
- $*max\_element(all(lcp)) = lcp[i]$ 일 때
- 반복 문자열 최대길이 $= lcp[i]$
- 반복 문자열 $= s[sa[i]:sa[i] + lcp[i] - 1]$
코드 2.
#include <bits/stdc++.h> using namespace std; #define all(v) (v).begin(), (v).end() #define rall(v) (v).rbegin(), (v).rend() int main() { ios_base::sync_with_stdio(false); cin.tie(NULL); int L; cin >> L; string s; cin >> s; auto buildSA = [&](string& s) { int N = s.size(); vector<int> sa(N), r(2 * N), nr(2 * N); for (int i = 0; i < N; ++i) { sa[i] = i; r[i] = s[i]; } for (int d = 1; d < N; d <<= 1) { auto cmp = [&](int i, int j) { return r[i] < r[j] || (r[i] == r[j] && r[i + d] < r[j + d]); }; sort(all(sa), cmp); for (int i = 1; i < N; ++i) { nr[sa[i]] = nr[sa[i - 1]] + cmp(sa[i - 1], sa[i]); } r = nr; } return sa; }; auto buildLCP = [&](string& s) { int N = s.size(); auto sa = buildSA(s); vector<int> lcp(N), isa(N); for (int i = 0; i < N; ++i) { isa[sa[i]] = i; } for(int k = 0, i = 0;i < N;++i) { if(isa[i]){ for(int j = sa[isa[i]-1]; max(i, j) + k < N && s[i+k] == s[j+k]; ++k); lcp[isa[i]] = (k ? k-- : 0); } } return lcp; }; auto lcp = buildLCP(s); cout << *max_element(all(lcp)) << '\n'; }
728x90
'PS > BOJ' 카테고리의 다른 글
백준 11408번: 열혈강호 5 (0) | 2022.02.22 |
---|---|
백준 9248번: Suffix Array (0) | 2022.02.21 |
백준 2316번: 도시 왕복하기 2 (0) | 2022.02.20 |
백준 13537번: 수열과 쿼리 1 (0) | 2022.02.19 |
백준 1126번: 같은 탑 (0) | 2022.02.18 |