2020牛客暑期多校训练营(第二场)All with Pairs
题目大意:
给你一个n个字符串,分别是 (s_1、s_2、s_3...s_n) 定义 (f(a,t)) 等于最大的一个 (i) ,使得 (a[1]a[2]...a[i]=t[|t|-i+1]...t[|t|])
求 : (sum_{i=1}^{n}{sum_{j=1}^{n}{f(s_i,s_j)^2}} (mod: 998244353))
题解:
这个也是看了题解补的。
-
首先预处理一下所有的后缀,用hash存下来,然后枚举前缀,求出每一个长度相同前后缀的数量。
-
但是这样写会出现重复计数的问题,比如有两个 (aba) ,那么对于这个会计算两个前缀,一个 (a) 一个 (aba) ,所以就会出现问题。
-
所以接下来就要解决重复计数的问题,这个可以用 (kmp) 的 (next) 数组来解决。字符串 (p) 的 (next[i]=j) 表示的是 (p_1p_2...p_{j-1}=p_{i-j+1}...p_i)
-
(cnt[i]) 表示长度为 (i) 的相同前缀后缀的数量,所以去重就是从前往后 (cnt[i-next[i]]-=cnt[i])
这个题目好像可以用AC自动机写,我先去学学,之后再补。
#include <bits/stdc++.h>
#define debug(x) printf("debug:%s=%d
",#x,x);
//#define debug(x) cout << #x << ": " << x << endl;
using namespace std;
typedef long long ll;
const int maxn = 1e6+10;
const int MOD = 998244353;
const ll p = 27;
int nxt[maxn];
void getNext(string p){
nxt[0] = -1;
int i = 0, j = -1, len = p.size();
while (i < len){
if (j == -1 || p[i] == p[j]) {
++i,++j,nxt[i]=j;
}
else j = nxt[j];
}
}
typedef unsigned long long ull;
map<ull,ll>mp;
string s[maxn];
ll cnt[maxn];
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
cin>>s[i];
int len = s[i].size();
ull sum=0,now=1;
for(int j=len-1;j>=0;j--){
sum = ((s[i][j]-'a'+1)*now+sum);
now=now*p;
mp[sum]++;
}
}
ll ans=0;
for(int i=1;i<=n;i++){
int len = s[i].size();
for(int j=0;j<=len+10;j++) cnt[j]=0,nxt[j]=0;
s[i]+='*';
getNext(s[i]);
ull sum=0;
for(int j=0;j<len;j++){
sum = (sum*p+s[i][j]-'a'+1);
cnt[j]+=mp[sum];
}
for(int j=0;j<len;j++){
nxt[j]=nxt[j+1]-1;
if(nxt[j]!=-1) cnt[nxt[j]]-=cnt[j];
}
for(int j=0;j<len;j++){
ans=(ans+(j+1)*1ll*(j+1)*cnt[j])%MOD;
}
}
printf("%lld
",ans);
return 0;
}
/*
4
bc
deedd
e
bdd
bc deedd e bdd
bc 2 0 0 0
deedd 0 5 0 1
e 0 0 1 0
bdd 0 0 0 3
2
aac
abdec
aac adbec
aac 3 0
abdec 0 5
4
abab
bcded
f
cffab
abab bcded f cffab
abab 4 0 0 2 20
bcded 1 5 0 1 20+27=47
f 0 0 1 0 47+1=48
cffab 0 0 0 5 48+25=73
*/