题目大意
给定
N
个串
N≤105
ci ≤107
∑ti≤5∗105
解题思路
对于这种题,第一个想到的就是先把串连起来构建自动机。然后把每个串的贡献放到SAM的节点上,沿fail链把贡献传下去这样就可以实现根题目中式子一样的功能,那么最后查询每个节点的答案(当然长度用的肯定是最大值),找到一个最有节点就是我们要找的 S <script type="math/tex" id="MathJax-Element-651">S</script>所对应的节点,它的权值就是SAM点权值的大小。
程序
//YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 5e5 + 5;
struct SAM{
int Go[26], fail, Len;
LL Val;
} A[MAXN * 2];
char S[MAXN], T[MAXN];
int N, tot, Start[MAXN], Root, p, Val, Ord[MAXN * 2];
void Add(int ch) {
int np = ++ tot;
A[np].Len = A[p].Len + 1;
for (; p && !A[p].Go[ch]; p = A[p].fail) A[p].Go[ch] = np;
if (!p) A[np].fail = Root; else {
int q = A[p].Go[ch];
if (A[p].Len + 1 == A[q].Len) A[np].fail = q; else {
int nq = ++ tot;
A[nq] = A[q];
A[q].fail = A[np].fail = nq;
A[nq].Len = A[p].Len + 1;
for (; p && A[p].Go[ch] == q; p = A[p].fail) A[p].Go[ch] = nq;
}
}
p = np;
}
bool Cmp(int x, int y) {return A[x].Len > A[y].Len;}
void Solve() {
for (int i = 1; i <= N; i ++) {
scanf("%d", &Val);
p = 1;
for (int j = Start[i]; j < Start[i + 1]; j ++) {
p = A[p].Go[T[j] - 'a'];
A[p].Val += Val;
}
}
for (int i = 1; i <= tot; i ++) Ord[i] = i;
sort(Ord + 1, Ord + 1 + tot, Cmp);
for (int i = 1; i <= tot; i ++) {
int Now = Ord[i];
A[A[Now].fail].Val += A[Now].Val;
}
LL Ans = 0;
for (int i = 1; i <= tot; i ++)
Ans = max(Ans, A[i].Val * A[i].Len);
printf("%I64d", Ans);
}
int main() {
scanf("%d", &N);
tot = 0;
for (int i = 1; i <= N; i ++) {
Start[i] = tot + 1;
char ch = getchar();
while (ch < 'a' || ch > 'z') ch = getchar();
while (ch >= 'a' && ch <= 'z') T[++ tot] = ch, ch = getchar();
}
Start[N + 1] = tot + 1;
Root = tot = 1;
for (int i = 1; i <= N; i ++) {
p = 1;
for (int j = Start[i]; j < Start[i + 1]; j ++) Add(T[j] - 'a');
}
Solve();
}