백준8235 prefixuffix

문제 링크

  • http://icpc.me/8235

문제 출처

  • 2011/2012 POI Stage 3 6번

사용 알고리즘

  • Hashing

시간복잡도

  • $O(N)$

풀이

Prefix와 Suffix를 각각 어떤 문자열 A,B에 대해 A+B와 B+A로 나타낼 수 있습니다.
주어진 문자열 T를 T = P + X + S로 분할하는 문제를 T = A + B + X + B + A로 분할하는 문제로 바꿀 수 있습니다.

Naive한 풀이로는 가능한 모든 A에 대해 가능한 모든 B를 보는 것으로, 해싱을 이용해 $O(N^2)$에 해결할 수 있습니다. 이 풀이를 $O(N)$ 풀이로 발전시킬 수 있습니다.


위 그림에서 A를 초록색 길이만큼 고정시켰을 때, 가능한 가장 긴 B를 파란색이라고 합시다.
잘 생각해보면, 현재 상황에서 A가 한 칸 줄어들 때 B는 최대 두 칸 늘어날 수 있습니다. 이 점을 이용하면 A의 끝점을 나타내는 포인터와 B의 끝점을 나타내는 포인터 모두 O(N)번 움직이게 해서 $O(N)$에 풀 수 있습니다.

전체 코드

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
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

int n, ans; string s;

const ll p = 917, mod = 993244853;
struct Hashing{
    vector<ll> ha, ba;
    Hashing(){ }
    Hashing(string &s) {
        int n = s.size();
        ha.resize(n+1); ba.resize(n+1);
        ba[0] = 1, ba[1] = p;
        for(int i=n-1; i>=0; i--) ha[i] = (ha[i+1] * p + s[i]) % mod;
        for(int i=2; i<=n; i++) ba[i] = ba[i-1] * p % mod;
    }
    int substr(int l, int r){
        ll ret = ha[l] - ha[r+1] * ba[r-l+1];
        ret %= mod; ret += mod; ret %= mod;
        return ret;
    }
}h1;

inline int same(int s, int e, int ss, int ee){
    return h1.substr(s, e) == h1.substr(ss, ee);
}

int main(){
    ios_base::sync_with_stdio(0); cin.tie(0);
    cin >> n >> s; s = "#" + s;
    h1 = Hashing(s);

    int j = 0;
    for(int i=n/2; i; i--){
        j = min(n/2-i, j+2);
        if(!same(1, i, n-i+1, n)) continue;
        ans = max(ans, i);
        while(j >= 0 && !same(i+1, i+j, n-i+1-j, n-i)) j--;
        ans = max(ans, i + j);
    }
    cout << ans;
}