题解:【模板】后缀自动机(SAM)
后缀数组偏技巧,一般都需要和其他的数据结构或算法结合使用,但是同时也更加灵活,可以解决更多更复杂的问题。
后缀自动机偏暴力,一般问题可以直接无脑上,但是对于复杂的问题可能会无能为力。
伟大的 lcx 曾经说过:如何学习后缀数组?只需要把所有 SAM 的题目做一遍就可以了。
题意分析
发现我们不会 SAM,考虑 SA 做法。
考虑枚举子串长度 \(\textit{len}\)。若 \(\textit{height}_i\geq\textit{len}\),说明 \(s[\textit{sa}_{i-1},n],s[\textit{sa}_i,n]\) 的公共前缀长度已经 \(\geq\textit{len}\),在 \(\textit{len}\) 变小的时候不变。
因此考虑从大到小枚举 \(\textit{len}\),\(\textit{height}_i\geq\textit{len}\) 说明 \(s[\textit{sa}_{i-1},n],s[\textit{sa}_i,n]\) 在接下来的过程中完全相同,可以并查集合并到一起维护。
再特判一下子串出现次数为 \(1\) 即可。
AC 代码
//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
typedef long long ll;
constexpr const int N=1e6,V=127;
int n,sa[N+1],rk[N<<1|1],height[N+1];
char s[N+1+1];
void buildSA(){
static int sa0[N+1],rk0[N+1],cnt[N+1];
for(int i=1;i<=n;i++){
cnt[rk[i]=s[i]]++;
}
for(int i=1;i<=V;i++){
cnt[i]+=cnt[i-1];
}
for(int i=1;i<=n;i++){
sa[cnt[rk[i]]--]=i;
}
for(int i=1;i<=n;i++){
rk0[i]=rk[i];
}
for(int p=0,i=1;i<=n;i++){
if(rk0[sa[i]]==rk0[sa[i-1]]){
rk[sa[i]]=p;
}else{
rk[sa[i]]=++p;
}
}
for(int w=1;w<n;w<<=1){
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=n;i++){
sa0[i]=sa[i];
}
for(int i=1;i<=n;i++){
cnt[rk[sa0[i]+w]]++;
}
for(int i=1;i<=n;i++){
cnt[i]+=cnt[i-1];
}
for(int i=n;1<=i;i--){
sa[cnt[rk[sa0[i]+w]]--]=sa0[i];
}
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=n;i++){
sa0[i]=sa[i];
}
for(int i=1;i<=n;i++){
cnt[rk[sa0[i]]]++;
}
for(int i=1;i<=n;i++){
cnt[i]+=cnt[i-1];
}
for(int i=n;1<=i;i--){
sa[cnt[rk[sa0[i]]]--]=sa0[i];
}
for(int i=1;i<=n;i++){
rk0[i]=rk[i];
}
for(int p=0,i=1;i<=n;i++){
if(rk0[sa[i]]==rk0[sa[i-1]]&&rk0[sa[i]+w]==rk0[sa[i-1]+w]){
rk[sa[i]]=p;
}else{
rk[sa[i]]=++p;
}
}
}
}
void buildHeight(){
for(int i=1,k=0;i<=n;i++){
k=max(k-1,0);
while(s[i+k]==s[sa[rk[i]-1]+k]){
k++;
}
height[rk[i]]=k;
}
}
struct dsu{
int f[N+1],size[N+1],Max;
int find(int x){
if(f[x]!=x){
return f[x]=find(f[x]);
}
return x;
}
void merge(int x,int y){
x=find(x),y=find(y);
if(size[x]<size[y]){
f[x]=y;
size[y]+=size[x];
Max=max(Max,size[y]);
}else{
f[y]=x;
size[x]+=size[y];
Max=max(Max,size[x]);
}
}
void build(int n){
for(int i=1;i<=n;i++){
f[i]=i;
size[i]=1;
}
Max=1;
}
int query(){
if(Max==1){
return 0;
}
return Max;
}
}dsu;
int main(){
/*freopen("test.in","r",stdin);
freopen("test.out","w",stdout);*/
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>(s+1);
n=strlen(s+1);
buildSA();
buildHeight();
ll ans=0;
dsu.build(n);
priority_queue<pair<int,int>>q;
for(int i=1;i<=n;i++){
q.push({height[i],i});
}
for(int len=n;1<=len;len--){
while(q.size()&&q.top().first>=len){
int i=q.top().second;
dsu.merge(sa[i],sa[i-1]);
q.pop();
}
ans=max(ans,1ll*len*dsu.query());
}
cout<<ans<<'\n';
cout.flush();
/*fclose(stdin);
fclose(stdout);*/
return 0;
}

浙公网安备 33010602011771号