考虑先把\(SAM\)建出来。
然后考虑怎么统计答案。
首先考虑每个等价类,如果他没有儿子的话,那么他必定只出现了一次。而对于不是叶子节点的等价类,该等价类出现的次数为儿子出现次数的和。
那么我们只要在link树上\(dfs\)就行了,考虑每个等价类内挑最长的子串长度即\(len_u\)来计入答案。
【模板】后缀自动机 (SAM)
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll int
#define N 1000005
#define fa link
ll ch[N << 1][30];//tire树的转移
ll link[N << 1],len[N << 1],v[N << 1];
ll node = 1,lst = 1;
ll cnt,head[N << 1];
struct P{int to,next;}e[N << 1];
inline void insert(int c){
int p = lst,q = ++node;lst = q;
len[q] = len[p] + 1,v[q] = 1;
while(!ch[p][c] && p != 0){//向上找
ch[p][c] = q;
p = link[p];
}
if(p == 0)
link[q] = 1;
else{
int x = ch[p][c];
if(len[p] + 1 == len[x]){
link[q] = x;
}else{
int y = ++ node ;//复制一个新节点
link[y] = link[x];
link[x] = link[q] = y;
len[y] = len[p] + 1;
std::memcpy(ch[y],ch[x],sizeof(ch[x]));
while(p != 0 && ch[p][c] == x){
ch[p][c] = y;
p = link[p];
}
}
}
}
inline void add(int x,int y){
e[++cnt].to = y;
e[cnt].next = head[x];
head[x] = cnt;
}
char s[N];
ll ans;
inline void dfs(int u){
if(head[u] == 0)
v[u] = 1;
else{
for(int i = head[u];i;i = e[i].next){
dfs(e[i].to);
v[u] += v[e[i].to];
}
ans = std::max(ans,len[u] * v[u]);
}
}
int main(){
freopen("q.in","r",stdin);
freopen("q.out","w",stdout);
scanf("%s",s + 1);
ll l = std::strlen(s + 1);
for(int i = 1;i <= l;++i)
insert(s[i] - 'a');
for(int i = 1;i <= node;++i)
add(link[i],i);
dfs(1);
std::cout<<ans<<std::endl;
}