宫水三叶认为,名字相似是一件非常神奇的事情。
三叶认为每一个人的名字都有特殊的含义,而两个人名字越相似,则从某种意义上就有一定联系。
三叶认为两个名字的联系是有向的,如果我们把名字看成一个字符串,那么我们定义 f ( s , t ) f(s,t) f(s,t) 为名字 s s s 对于名字 t t t 的联系。
三叶这样定义 f ( s , t ) f(s,t) f(s,t):假设存在最大的 i i i 满足 s [ 1 … i ] = t [ ∣ t ∣ − i + 1 … ∣ t ∣ ] s[1\dots i] = t[|t|-i+1 \dots |t|] s[1…i]=t[∣t∣−i+1…∣t∣] ,那么 f ( s , t ) f(s,t) f(s,t) 的值为 i i i ,否则 f ( s , t ) f(s,t) f(s,t) 的值为 0 0 0 。
换句话说, f ( s , t ) f(s,t) f(s,t) 为最长的 i i i 是 s s s 长度为 i i i 的前缀和 t t t 长度为 i i i 的后缀相等。
三叶看到了一个班级的名单,这个班级共有 n n n 名同学,第 i i i 名同学的名字为 s i s_i si。
三叶想算一算整个班级的凝聚力是多少。一个班级的凝聚力定义为 ∑ i = 1 n ∑ j = 1 n f ( s i , s j ) 2 \sum_{i=1}^{n} \sum_{j=1}^{n} f(s_i,s_j)^2 ∑i=1n∑j=1nf(si,sj)2 。
不过三叶只想计算这个值对 998244353 998244353 998244353 取模后的结果。
第一行一个整数 n n n 。
接下来 n n n 行,每行一个字符串 s i s_i si ,表示一个人的名字。
一行一个整数,表示答案。
样例输入 1
3
ab
ba
aba
样例输出 1
29
样例数据 2
见下发文件。
本题采用捆绑测试。
对于所有数据,满足 1 ≤ n ≤ 1 0 5 , 1 ≤ ∑ ∣ s i ∣ ≤ 1 0 6 1\le n \le 10^5,1\le \sum |s_i| \le 10^6 1≤n≤105,1≤∑∣si∣≤106。
保证字符串均为小写字母。
我们记 S = ∑ ∣ s i ∣ S= \sum |s_i| S=∑∣si∣。
子任务编号 | n n n | S S S | 特殊性质 | 分值 |
---|---|---|---|---|
1 1 1 | ≤ 1 0 2 \le 10^2 ≤102 | $ \le 10^2$ | − - − | 5 5 5 |
2 2 2 | ≤ 1 0 3 \le 10^3 ≤103 | ≤ 1 0 3 \le 10^3 ≤103 | − - − | 25 25 25 |
3 3 3 | ≤ 1 0 5 \le 10^5 ≤105 | ≤ 1 0 5 \le 10^5 ≤105 | 特殊性质 A | 20 20 20 |
4 4 4 | ≤ 1 0 5 \le 10^5 ≤105 | ≤ 1 0 5 \le 10^5 ≤105 | 特殊性质 B | 20 20 20 |
5 5 5 | ≤ 1 0 5 \le 10^5 ≤105 | ≤ 1 0 6 \le 10^6 ≤106 | − - − | 30 30 30 |
特殊性质 A:保证
∣
s
i
∣
≤
10
|s_i|\le 10
∣si∣≤10。
特殊性质 B:不保证
∣
s
i
∣
|s_i|
∣si∣ 随机,但是字符串的每一位随机取
a
∼
z
a\sim z
a∼z 中的一个。
题解:
首先,
∣
s
i
∣
|s_i|
∣si∣ 个前缀和后缀。若对于每个前缀和后缀都匹配,必然算重。这时考虑去重。
先算出
k
m
p
kmp
kmp 的
n
e
x
[
]
nex[]
nex[] 数组,这时我们可以对于每一个前缀扣除
n
e
x
nex
nex 的匹配数,就是答案。
#include<bits/stdc++.h>
#define N 1000005
#define ull unsigned long long
using namespace std;
const ull P=131313131;
const int mod=998244353;
inline int read(){
int x=0,f=1;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
return x*f;
}
char s[N];
vector<int> a[N];
ull p[N];
map<ull, int> h;
int cnt[N],nex[N];
inline int power(int x,int c){
int now=1;
while(c){
if(c&1)now=1ll*now*x%mod;
x=1ll*x*x%mod;c>>=1;
}
return now;
}
int main(){
int n=read();
p[0]=1;
for(int i=1;i<=1000000;++i)p[i]=p[i-1]*P;
for(int i=1;i<=n;++i){
scanf("%s",1+s);
int len=strlen(1+s);
a[i].push_back(len);
for(int j=1;j<=len;++j)a[i].push_back(s[j]-'a'+1);
ull now=0;
for(int j=len;j;--j){
now=(ull)p[len-j]*a[i][j]+now;
h[now]++;
}
}
int ans=0;
for(int i=1;i<=n;++i){
ull now=0;
nex[1]=0;now=a[i][1];
if(h.find(now)!=h.end())cnt[1]=h[now];
else cnt[1]=0;
for(int j=2,k=0;j<=a[i][0];++j){
k=nex[j-1];
while(k&&a[i][k+1]!=a[i][j])k=nex[k];
if(a[i][k+1]==a[i][j])k++;
nex[j]=k;
now=now*P+a[i][j];
if(h.find(now)!=h.end())cnt[j]=h[now];
else cnt[j]=0;
cnt[nex[j]]-=cnt[j];
}
for(int j=1;j<=a[i][0];++j){
ans=(ans+1ll*j*j%mod*cnt[j]%mod)%mod;
}
}
printf("%d\n",(ans+mod)%mod);
return 0;
}