[2020牛客暑期多校训练营(第二场)All with Pairs]
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\n",#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\n",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
*/