2020牛客暑期多校训练营(第二场)A. All with Pairs

链接

https://ac.nowcoder.com/acm/contest/5667/A

题意

有 $n$ 个字符串

定义 $f(s,t)$ 为最大的 $i$ 满足 $s_{1…i}=t_{|t|-i+1…|t|}$

求 $\sum_{i=1}^n\sum_{j=1}^n{f(s_i,s_j)^2}\pmod{998244353}$

思路

将每一个后缀 Hash ,再遍历每个字符串的前缀找相同的后缀的个数

但有可能找到的后缀来自同一个字符串,又因为这些后缀都是当前字符串的前缀,所以我们可以用 KMP 算法算出 next 数组找出当前位置的最大 border,从前往后执行 cnt[next[i+1]-1]-=cnt[i] 即可完成去重

最后计算答案即可

代码

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
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const int M=1e6+5;
const long long MOD=998244353;
const long long HASH_BASE=131;
const long long HASH_MOD[2]={1000173169,1000000007};
string s[N];
long long power[M][2],ha[M][2],res[M],ans;
int nxt[M];
map<pair<long long,long long>,int>mp;
void init() {
power[0][0]=power[0][1]=1;
for(int i=1;i<M;i++)
for(int j=0;j<2;j++) {
power[i][j]=power[i-1][j]*HASH_BASE%HASH_MOD[j];
}
}
void init_hash(string &s) {
int len=s.length();
ha[len][0]=ha[len][1]=0;
for(int i=len-1;i>=0;i--) {
for(int j=0;j<2;j++)
ha[i][j]=(s[i]*power[len-i-1][j]%HASH_MOD[j]+ha[i+1][j])%HASH_MOD[j];
mp[make_pair(ha[i][0],ha[i][1])]++;
}
}
void kmp(string &s) {
int len=s.length();
int i=0,j=-1;
nxt[0]=-1;
while(i<len) {
if(j==-1||s[i]==s[j]) i++,j++,nxt[i]=j;
else j=nxt[j];
}
}
void cal(string &s) {
int len=s.length();
for(int i=0;i<len;i++) {
for(int j=0;j<2;j++) {
if(i==0) ha[i][j]=s[i];
else ha[i][j]=(ha[i-1][j]*HASH_BASE%HASH_MOD[j]+s[i])%HASH_MOD[j];
}
res[i]=mp[make_pair(ha[i][0],ha[i][1])];
}
kmp(s);
for(int i=0;i<len;i++) if(nxt[i+1]>0) res[nxt[i+1]-1]-=res[i];
for(int i=0;i<len;i++) if(res[i]>0) ans=(ans+1ll*(i+1)*res[i]%MOD*(i+1)%MOD)%MOD;
}
int main() {
init();
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++) {
cin>>s[i];
init_hash(s[i]);
}
for(int i=1;i<=n;i++) cal(s[i]);
printf("%lld\n",ans);
return 0;
}