코딩/백준 문제 풀이

BOJ 19265 - Is it a p-drome?

stonejjun 2022. 8. 27. 04:39

문제 링크 : https://www.acmicpc.net/problem/19265

문제 풀이

 이 문제는 배열 p에 존재한 모든 $p_i$에 대해서 $T_i = Tp_i $ 인지를 S의 모든 구간에 T에 대해서 확인하는 문제이다. 

 여기서 어떤 형태를 비교하는 거니까 문자열 매칭과 비슷한 느낌을 받을 수 있다. 먼저 생각난 것은 KMP 였지만 KMP로는 풀 수 없는 형태였다. 실패함수가 좋은 형태로 나타나지 않기 때문에 KMP는 포기할 수 밖에 없었다. 

 이 상태에서 문제를 푸는 사고의 흐름이었다면 라빈-카프 해싱을 생각을 했어야 했다. $T_i = Tp_i $ 라는 것은 i 기반 해싱값과 pi 기반 해싱값이 같다는 거고, 좀 만 더 생각하고 식을 변형 시켜 보면 $\sum_{i}^{} i\times T_i = \sum_{i}^{} p_i\times T_i$ 이어야만 구간이 문제 조건을 만족한다는 것을 알 수 없다. 하지만 이 식을 만족해도 문제 조건을 만족한다는 보장이 없기 때문에 $ \sum_{i}^{} A_i\times i\times T_i = \sum_{i}^{}A_i\times p_i\times T_i $ 의 식으로 확장시켜서 생각할 수 있고, 이 식에서는 $A_i$ 라는 변수를 추가되었고 이를 마음대로 변형시켜서 여러 번 확인하는 것을 할 수 있다. 

하지만 문제가 있다. S의 모든 길이 m의 부분 구간 T에 대해서 위의 식의 좌변 과 우변의 값을 계산해야 한다. 이때 T의 길이가 m이며 sliding 한 형태로 존재한다는 것을 이용하여 FFT를 떠올릴 수 있다. T에 곱해질 값을 순서를 뒤집은 다음에 두 다항식을 곱하게 되면 위의 한 쪽 변이 곱해진 다항식의 한 항의 계수로써 등장하게 된다. 따라서 $O(N^2)$ 이 아닌 $O(NlgN)$ 에 문제를 해결 할 수 있다. 

나는 해싱을 생각을 못했기 때문에 FFT 라는 단어를 힌트로 들었고, 조건을 다른 형태의 식으로 바꾸었다. $ \sum_{i}^{} A_i\times T_i = 0 $ 를 확인할 건데 $A_i$  를 구성할 때   $A_i$ 에 k를 더하고 $A_pi$ 에 -k를 하는 것을 무작위 적으로 반복하면 위의 식을 만족해야 문제의 조건을 만족한다고 할 수 있다. 이를 이용하여 똑같이 A 배열을 구성하고 fft를 돌려서 시간복잡도를 맞추어 주면 문제를 해결 할 수 있다. 아래의 코드는 이 풀이 기반 코드이다.  

코드

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
#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef double dl;
typedef pair<dl,dl> pdi;
typedef pair<ll,ll> pii;
typedef pair<ll,pii> piii;
 
#define ff first
#define ss second
#define eb emplace_back
#define ep emplace
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(), (x).end()
#define compress(v) sort(all(v)), v.erase(unique(all(v)), v.end())
#define IDX(v, x) lower_bound(all(v), x) - v.begin()
//cout<<fixed;
//cout.precision(12);
 
struct poi{
    ll val,xx,yy;
};
 
 
namespace fft{
    using real_t = double;
    using cpx = complex<real_t>;
    void FFT(vector<cpx> &a, bool inv_fft = false){
        int N = a.size();
        vector<cpx> root(N/2);
        for(int i=1, j=0; i<N; i++){
            int bit = (N >> 1);
            while(j >= bit) j -= bit, bit >>= 1;
            j += bit;
            if(i < j) swap(a[i], a[j]);
        }
        real_t ang = 2 * acos(-1/ N * (inv_fft ? -1 : 1);
        for(int i=0; i<N/2; i++) root[i] = cpx(cos(ang * i), sin(ang * i));
        for(int i=2; i<=N; i<<=1){
            int step = N / i;
            for(int j=0; j<N; j+=i){
                for(int k=0; k<i/2; k++){
                    cpx u = a[j+k], v = a[j+k+i/2* root[step * k];
                    a[j+k] = u+v;
                    a[j+k+i/2= u-v;
                }
            }
        }
        if(inv_fft) for(int i=0; i<N; i++) a[i] /= N;
    }
    vector<ll> multiply_mod(const vector<int> &a, const vector<int> &b, const int mod){
        int N = 2while(N < a.size() + b.size()) N <<= 1;
        vector<cpx> v1(N), v2(N), r1(N), r2(N);
        for(int i=0; i<a.size(); i++) v1[i] = cpx(a[i] >> 15, a[i] & 32767);
        for(int i=0; i<b.size(); i++) v2[i] = cpx(b[i] >> 15, b[i] & 32767);
        FFT(v1); FFT(v2);
        for(int i=0; i<N; i++){
            int j = i ? N-i : i;
            cpx ans1 = (v1[i] + conj(v1[j])) * cpx(0.50);
            cpx ans2 = (v1[i] - conj(v1[j])) * cpx(0-0.5);
            cpx ans3 = (v2[i] + conj(v2[j])) * cpx(0.50);
            cpx ans4 = (v2[i] - conj(v2[j])) * cpx(0-0.5);
            r1[i] = (ans1 * ans3) + (ans1 * ans4) * cpx(01);
            r2[i] = (ans2 * ans3) + (ans2 * ans4) * cpx(01);
        }
        FFT(r1, true); FFT(r2, true);
        vector<ll> ret(N);
        for(int i=0; i<N; i++){
            ll av = llround(r1[i].real());
            ll bv = llround(r1[i].imag()) + llround(r2[i].real());
            ll cv = llround(r2[i].imag());
            av %= mod, bv %= mod, cv %= mod;
            ret[i] = (av << 30+ (bv << 15+ cv;
            ret[i] %= mod;
            ret[i] += mod;
            ret[i] %= mod;
        }
        return ret;
    }
}
 
vector<int> x;
ll n,m;
ll mod=998244353;
string s;
string t;
ll arr[1010101];
ll brr[1010101];
vector<ll> v[1010101];
vector<int> an;
vector<ll> res;
 
 
mt19937 rd((unsigned)chrono::steady_clock::now().time_since_epoch().count());
int grnd(ll a,ll b){
    uniform_int_distribution<int> rnd(a, b);
    return rnd(rd);
}
 
ll ans[1010101];
int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    ll i,j,k,a,b,c,d,e,f;
    cin>>n>>m>>k;
 
    for(i=1;i<=n;i++){
        ll idx=n-i;
        cin>>a;
        arr[idx]=n-a;
    }
 
    for(i=1;i<=m;i++){
        cin>>a;
        an.pb(a);
    }
    for(i=0;i<=m+n;i++){
        ans[i]=1;
    }
 
    for(ll p=1;p<=3;p++){
        x.clear();
        x.resize(n);
 
        for(i=1;i<=7*n;i++){
            ll f=grnd(0,n-1);
            ll val=grnd(1,p*100);
            x[min(f,arr[f])]+=val;
            x[max(f,arr[f])]-=val;
        }
 
        res=fft::multiply_mod(x, an, 998244353);
        for(i=n-1;i<m;i++){
            if(res[i]) ans[i]=0;
        }
    }
 
    for(i=n-1;i<m;i++){
        cout<<ans[i];
    }
 
}
 
cs

'코딩 > 백준 문제 풀이' 카테고리의 다른 글

BOJ 8189 - Antisymmetry  (0) 2022.09.16
BOJ 8103 - Rooks  (0) 2022.08.27
BOJ 10169 - 안전한 비상연락망  (0) 2022.08.26
BOJ 3682 - 동치 증명  (0) 2022.08.26
BOJ 7117 - Sevens, twos and zeros  (0) 2022.08.20