【刷题】洛谷 P3804 【模板】后缀自动机
题目描述
给定一个只包含小写字母的字符串 \(S\) ,
请你求出 \(S\) 的所有出现次数不为 \(1\) 的子串的出现次数乘上该子串长度的最大值。
输入输出格式
输入格式:
一行一个仅包含小写字母的字符串 \(S\)
输出格式:
一个整数,为所求答案
输入输出样例
输入样例#1:
abab
输出样例#1:
4
说明
对于 \(10\%\) 的数据, \(|S| \leq 1000\)
对于 \(100\%\) 的数据,\(|S| \leq 10^6\)
题解
SAM模板题
建出SAM后,统计子树和算每个状态最长子串的出现次数,根据贪心,由于题目求的是同一个子串的出现次数和长度的乘积,所以在同一个状态的子串,出现次数相同,那么只要选最长的就行了
于是基数排序搞出拓扑序,统计一下就好了
#include<bits/stdc++.h>
#define ui unsigned int
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
const int MAXN=1000000+10;
int n,las=1,tot=1,len[MAXN<<1],ch[MAXN<<1][30],fa[MAXN<<1],cnt[MAXN],rk[MAXN<<1],size[MAXN<<1];
ll ans;
char s[MAXN];
template<typename T> inline void read(T &x)
{
T data=0,w=1;
char ch=0;
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')w=-1,ch=getchar();
while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar();
x=data*w;
}
template<typename T> inline void write(T x,char ch='\0')
{
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);
putchar(x%10+'0');
if(ch!='\0')putchar(ch);
}
template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);}
template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);}
template<typename T> inline T min(T x,T y){return x<y?x:y;}
template<typename T> inline T max(T x,T y){return x>y?x:y;}
inline void extend(int c)
{
int p=las,np=++tot;
las=np;
len[np]=len[p]+1;
while(p&&!ch[p][c])ch[p][c]=np,p=fa[p];
if(!p)fa[np]=1;
else
{
int q=ch[p][c];
if(len[q]==len[p]+1)fa[np]=q;
else
{
int nq=++tot;
fa[nq]=fa[q];
memcpy(ch[nq],ch[q],sizeof(ch[nq]));
len[nq]=len[p]+1,fa[q]=fa[np]=nq;
while(p&&ch[p][c]==q)ch[p][c]=nq,p=fa[p];
}
}
size[np]=1;
}
int main()
{
scanf("%s",s+1);
n=strlen(s+1);
for(register int i=1;i<=n;++i)extend(s[i]-'a'+1);
for(register int i=1;i<=tot;++i)cnt[len[i]]++;
for(register int i=1;i<=n;++i)cnt[i]+=cnt[i-1];
for(register int i=1;i<=tot;++i)rk[cnt[len[i]]--]=i;
for(register int i=tot;i>=1;--i)
{
size[fa[rk[i]]]+=size[rk[i]];
if(size[rk[i]]>1)chkmax(ans,1ll*size[rk[i]]*len[rk[i]]);
}
write(ans,'\n');
return 0;
}