>Link
luogu P3804
>Description
给定一个只包含小写字母的字符串S,
请你求出 S 的所有出现次数不为 1 的子串的出现次数乘上该子串长度的最大值。
∣ S ∣ ≤ 1 0 6 |S|\le 10^6 ∣S∣≤106
>解题思路
在我题单里躺了好久,终于学了,我太蒻了qwq
(注意这里是有两个结构的,parent tree和后缀自动机,要根据它们的性质灵活运用)
这道题要求的是最大的出现次数*长度
对于一个节点,这里的串的 e n d p o s endpos endpos 都是相同的(出现次数相同),那要最大肯定优先取 l e n len len(最长的串的长度)
然后因为在parent tree中,父亲节点的 e n d p o s endpos endpos 是被几个儿子分割的,也就是 ∣ e n d p o s ( f a ) ∣ = ∑ ∣ e n d p o s ( s o n ) ∣ |endpos(fa)|=\sum |endpos(son)| ∣endpos(fa)∣=∑∣endpos(son)∣,这可以直接用个树形DP解决
>代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 2000010
#define LL long long
using namespace std;
struct node
{
int len, f, son[30];
node(){
memset (son, 0, sizeof (son)), len = f = 0;}
} t[N];
struct edge
{
int to, nxt;
} e[N];
int n, tot, cnt, h[N], f[N], last;
LL ans;
string s;
void add_edge (int u, int v) {
e[++cnt] = (edge){
v, h[u]}; h[u] = cnt;}
void add (int x)
{
int p = last, np = last = ++tot; //last按顺序加的上一个前缀的位置
t[np].len = t[p].len + 1;
f[tot] = 1; //只有新加一个字符时,endpos才会出现新的数值
for (; p && !t[p].son[x]; p = t[p].f) t[p].son[x] = np; //连边
if (!p) t[np].f = 1;
else
{
int q = t[p].son[x];
if (t[q].len == t[p].len + 1) t[np].f = q; //q是np的后缀,直接连边
else //新建一个点来平分q,使得nq是np的后缀且与q性质相同
{
int nq = ++tot;
t[nq] = t[q]; t[nq].len = t[p].len + 1;
t[q].f = t[np].f = nq;
for (; p && t[p].son[x] == q; p = t[p].f)
t[p].son[x] = nq;
}
}
}
void dfs (int now)
{
for (int i = h[now]; i; i = e[i].nxt)
{
dfs (e[i].to);
f[now] += f[e[i].to];
}
if (now != 1 && f[now] > 1)
ans = max (ans, (LL)f[now] * (LL)t[now].len);
}
int main()
{
// freopen ("P3804_2.in", "r", stdin);
cin >> s;
n = s.size(), s = " " + s;
tot = last = 1;
for (int i = 1; i <= n; i++) add (s[i] - 'a' + 1);
for (int i = 2; i <= tot; i++) add_edge (t[i].f, i);
dfs (1);
printf ("%lld", ans);
return 0;
}