题意
题解
构造后缀数组,按照 l c p lcp lcp 从大到小依次处理,同时使用并查集维护连通分量中的最大、最小值与后缀数量。此时可以统计出 l c p = h lcp = h lcp=h 时的方案数与美味度最大值。根据相似度的定义,最后求一遍后缀即可。总时间复杂度 O ( n log 2 n ) O(n\log^2 n) O(nlog2n)。
#include <bits/stdc++.h>
using namespace std;
constexpr int MAXN = 3E5 + 5;
using ll = long long;
struct SA
{
int n, k;
int sa[MAXN], lcp[MAXN], rnk[MAXN], tmp[MAXN];
void construct(string &s)
{
n = s.size();
for (int i = 0; i <= n; ++i)
sa[i] = i, rnk[i] = i < n ? s[i] : -1;
auto cmp = [&](int i, int j)
{
if (rnk[i] != rnk[j])
return rnk[i] < rnk[j];
int ri = i + k <= n ? rnk[i + k] : -1;
int rj = j + k <= n ? rnk[j + k] : -1;
return ri < rj;
};
for (k = 1; k <= n; k *= 2)
{
sort(sa, sa + n + 1, cmp);
tmp[sa[0]] = 0;
for (int i = 1; i <= n; ++i)
tmp[sa[i]] = tmp[sa[i - 1]] + (cmp(sa[i - 1], sa[i]) ? 1 : 0);
copy(tmp, tmp + n + 1, rnk);
}
int h = 0;
for (int i = 0; i < n; ++i)
{
int j = sa[rnk[i] - 1];
if (h > 0)
--h;
for (; i + h < n && j + h < n; ++h)
if (s[i + h] != s[j + h])
break;
lcp[rnk[i] - 1] = h;
}
}
} sa;
struct DSU
{
int n, par[MAXN], cnt[MAXN], mx[MAXN], mn[MAXN];
void init(int _n)
{
n = _n;
for (int i = 0; i < n; ++i)
par[i] = i;
}
int find(int x) { return par[x] == x ? x : (par[x] = find(par[x])); }
pair<ll, ll> unite(int x, int y)
{
x = find(x), y = find(y);
ll num = (ll)cnt[x] * cnt[y];
ll t = max((ll)mx[x] * mx[y], (ll)mn[x] * mn[y]);
par[x] = y;
cnt[y] += cnt[x];
mx[y] = max(mx[y], mx[x]), mn[y] = min(mn[y], mn[x]);
return {num, t};
}
} dsu;
int N, A[MAXN];
string S;
struct node
{
int h, i, j;
} ns[MAXN];
ll num[MAXN], mx[MAXN];
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> N >> S;
for (int i = 0; i < N; ++i)
cin >> A[i];
dsu.init(N), sa.construct(S);
for (int i = 0; i < N; ++i)
dsu.mx[i] = dsu.mn[i] = A[i], dsu.cnt[i] = 1;
for (int i = 0; i < N; ++i)
ns[i] = {sa.lcp[i], sa.sa[i], sa.sa[i + 1]};
sort(ns, ns + N, [](const node &a, const node &b)
{ return a.h > b.h; });
for (int i = 0; i < N; ++i)
mx[i] = LLONG_MIN;
for (int i = 0; i < N; ++i)
{
if (ns[i].i == N || ns[i].j == N)
continue;
auto p = dsu.unite(ns[i].i, ns[i].j);
int h = ns[i].h;
num[h] += p.first, mx[h] = max(mx[h], p.second);
}
for (int i = N - 2; i >= 0; --i)
num[i] += num[i + 1], mx[i] = max(mx[i], mx[i + 1]);
for (int i = 0; i < N; ++i)
cout << num[i] << ' ' << (num[i] != 0 ? mx[i] : 0) << '\n';
return 0;
}