通關密語
https://tioj.ck.tp.edu.tw/problems/1035
Description
給定兩個長度小於 $5 \times 10^4$ 的小寫英文字母字串 $S,T$
定義「最佳擬合」,就是將 $S$ 經過平移後和 $T$ 比對,同樣的字元數最多的那一種方法。
請輸出一個正整數,代表最佳擬合的方案下,相同的字元有幾個。
ex.
對於
ababa
babab
來說,
ababa
=babab
這是一種最佳擬合的方法,$S$經過向左平移之後$S,T$有四個位置的字元相同
Solution
naive的$n^2$做法可以AC本題,只要妥當控制常數即可
不過這裡提供一個NTT的$\mathcal{O}(C\cdot n\log n)$解
假設$S$對$T$的平移量是$x$(可以為負的),題目所求為
$$
\sum _ {i-j = x} [S_i = T_j]
$$
的最大值
那我們枚舉26種英文字母,可以寫成
$$
\sum _ {c \in \sigma} \sum _ {i-j = x} [S_i = c] \cdot [T_j = c]
$$
令$F_i = [S_i = c], G_j = [T _ {-j} = c]$
答案便是
$$
R_x = \sum _ {i+j = x} F_i G_j = F * G
$$
最後取$R$的最大值就好了,答案不會大於字串長度所以模數只要不要超小就不用關心答案被mod到
每次捲積交給NTT可以$\mathcal{O}(n\log n)$做完
索引值取負號可以用直接反轉字串取代,因為本題不關心偏移量$x$是多少
另外這題用FFT好像比較快,模的常數真的有點大
有一個常數優化是沒有出現過的字母就不需要NTT
AC code
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
| #pragma g++ optimize("Ofast")
#pragma loop_opt(on)
#include <bits/stdc++.h>
using namespace std;
const int64_t m = 998244353, g = 3, N = 1<<17;
int rev[N];
int64_t modpow(int64_t e,int64_t p) {
int64_t r = 1;
while(p) (p&1)&&(r=r*e%m), e=e*e%m, p>>=1;
return r;
}
void NTT(int64_t F[],int n,bool inv) {
for(int i = 0, L = __lg(n); i < n; i++) {
rev[i] = (rev[i>>1]>>1) | ((i&1)<<L-1);
if(i < rev[i]) swap(F[i],F[rev[i]]);
}
for(int step = 1; step < n; step *= 2) {
int64_t root = modpow(g,(m-1)/(step*2));
if(inv) root = modpow(root,m-2);
for(int i = 0; i < n; i += step*2) {
int64_t now = 1;
for(int j = 0; j < step; j++) {
int64_t a = F[i+j];
int64_t b = F[i+j+step]*now%m;
F[i+j] = (a+b)%m;
F[i+j+step] = (a-b+m)%m;
now = now*root%m;
}
}
}
if(inv) {
int64_t in = modpow(n,m-2); // inv of n
for(int i = 0; i < n; i++) F[i] = F[i]*in%m;
}
}
void mul(int64_t A[],int64_t B[],int64_t C[],int n) {
NTT(A,n,0);
NTT(B,n,0);
for(int i = 0; i < n; i++) C[i] = A[i]*B[i]%m;
NTT(C,n,1);
}
int64_t A[N],B[N],C[N],ans[N];
signed main() {
ios_base::sync_with_stdio(0), cin.tie(0);
string s,t;
cin >> s >> t;
int n = 1<<__lg(s.size()+t.size())+1;
for(int c = 0; c < 26; c++) {
for(int i = 0; i < n; i++) {
A[i] = (i < s.size() && s[i]-'a'==c);
B[i] = (i < t.size() && t[t.size()-1-i]-'a'==c);
}
mul(A,B,C,n);
for(int i = 0; i < n; i++) ans[i] += C[i];
}
cout << *max_element(ans,ans+n) << '\n';
}
|