这A感觉挺水的啊,为什么没什么人写(怕是大家都自闭了。
首先计算所有字符串每个后缀的hash值,记录每个hash值的出现次数。
对于每个字符串的每个前缀,我们记录与其hash值相同的后缀个数,但由于我们只对最长的符合要求的前缀计数,我们还要对该个数进行筛选。
假设我们的后缀集中有2个a,2个aba,1个ababa,我们对ababa进行匹配,前缀a能与a,aba,ababa匹配,共出现5次,同理aba出现3次,ababa出现1次。不难发现,若,因为,所以。同理...
所以我们只须做一遍从前到后做一遍即可排除重复情况(为前缀1~i出现的次数)
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) ((x)&(-x))
#define REP(i, a, n) for(int i=a;i<=(n);i++)
#define IOS ios::sync_with_stdio(false),cin.tie(0), cout.tie(0)
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> P;
const int maxn = 1e5 + 10;
const int N = 1e6 + 10;
const int M = 1e3 + 10;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const int mod2 = 998244353;
const int mod3 = 1e9 + 9;
const int hash1 = 131;
const int hash2 = 13331;
const double eps = 1e-6;
unordered_map<ull, int> cnt;
ull p[N], f[N];
string a[N];
int nxt[N];
void getnext(string &a)
{
int len = a.length();
nxt[1] = 0;
for (int i = 2, j = 0; i < len; i++)
{
while (j && a[i] != a[j + 1])
j = nxt[j];
if (a[j + 1] == a[i])
j++;
nxt[i] = j;
}
}
void prework(string &a)
{
ull val = 0;
int t = 0;
for (int i = a.length() - 1; i >= 1; i--)
{
val = val + (a[i] - 'a' + 1) * p[t++];
cnt[val]++;
}
}
int cntpre[N];
ll ans;
void work(string &a)
{
getnext(a);
ull val = 0;
for (int i = 1; i <= a.length() - 1; i++)
{
val = val * hash1 + a[i] - 'a' + 1;
cntpre[i] = cnt[val];
}
for (int i = 1; i <= a.length() - 1; i++)
{
cntpre[nxt[i]] -= cntpre[i];
}
for (int i = 1; i <= a.length() - 1; i++)
{
ans += (ll) i * i * cntpre[i];
ans %= mod2;
}
}
int main()
{
IOS;
int n;
cin >> n;
p[0] = 1;
for (int i = 1; i < N; i++)
{
p[i] = p[i - 1] * hash1;
}
for (int i = 1; i <= n; i++)
{
cin >> a[i];
a[i] = "@" + a[i];
prework(a[i]);
}
for (int i = 1; i <= n; i++)
{
work(a[i]);
}
cout << ans << endl;
return 0;
}