链接:https://www.luogu.org/problem/P3804
题意:
给定一个只包含小写字母的字符串S,
请你求出 S 的所有出现次数不为 1 的子串的出现次数乘上该子串长度的最大值。
思路:后缀自动机的模板练习题,求解一个字符串中每个子串出现了几次可以用后缀自动机加上拓扑排序来求解,子节点中出现的字符串在其fa节点中一定也出现过,所以从拓扑序最大的往上更新fa节点的出现次数(size数组存储),实现这一更新的方式可以求拓扑序后for循环更新,也可以利用fa指针建立出一棵树,然后在这棵树上dfs更新size。求出每个节点的size后,该节点的最长长度就是该节点的len值,所以对每一个size大于1的节点,用其size乘以其len的值就可以更新最大答案了。
dfs实现代码:
#include<bits/stdc++.h>
#define endl '\n'
#define css(n) cout<<setiosflags(ios::fixed)<<setprecision(n);
#define sd(a) scanf("%d",&a)
#define sld(a) scanf("%lld",&a)
#define m(a,b) memset(a,b,sizeof a)
#define pb push_back
#define lson id<<1
#define rson id<<1|1
typedef long long ll;
using namespace std;
const int maxn = 2e6,modd = 1e9 + 7,inf = 0x3f3f3f3f,INF = 0x7fffffff;
inline ll min(ll a,ll b){return a < b ? a : b;}
inline ll max(ll a,ll b){return a > b ? a : b;}
int n,m,t;
char s[maxn];
int size[maxn];
struct node
{
int to,next;
}edge[2*maxn];
int e,head[maxn];
void edge_init()
{
memset(head,0,sizeof(head));
e=0;
}
void add_edge(int u,int v)
{
edge[++e].to=v;
edge[e].next=head[u];
head[u]=e;
}
/*for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
}*/
//int vis[maxn];
struct SAM
{
int next[maxn][30],fa[maxn],len[maxn];
int root,tot,last;
int newnode(int l)//创建一个新的状态节点
{
fa[tot]=-1;
for(int i=0;i<30;++i) next[tot][i]=-1;
len[tot++]=l; return tot-1;
}
void init()//后缀自动机的初始化
{
tot=0;
last=root=newnode(0);
}
void extend(int x)//加入一个新的字符 //sam.extend(s[i]-'a');
{
int p=last; int cur=newnode(len[p]+1);
size[cur]=1;
while(p!=-1&&next[p][x]==-1){
next[p][x]=cur; p=fa[p];
}
if(p==-1) fa[cur]=root;
else
{
int q=next[p][x];
if(len[q]==len[p]+1) fa[cur]=q;
else
{
int tmp = newnode(len[p]+1);
memcpy(next[tmp],next[q],sizeof(next[q]));
fa[tmp]=fa[q]; fa[q]=fa[cur]=tmp;
while(p!=-1&&next[p][x]==q){
next[p][x]=tmp; p=fa[p];
}
}
}
last=cur;
}
void bt()
{
for(int i=1;i<tot;i++)
{
// edge[fa[i]].push_back(i);
add_edge(fa[i],i);
}
}
void dfs(int x)
{
for(int i=head[x];i;i=edge[i].next)
{
int y=edge[i].to;
dfs(y);
size[x]+=size[y];
}
return;
}
ll query()
{
ll fin=0;
for(int i=0;i<tot;i++)
{
if(size[i]>1) fin=max(fin,size[i]*len[i]);
}
return fin;
}
}sam;
int main()
{
scanf("%s",s+1);
n=strlen(s+1);
sam.init();
for(int i=1;i<=n;i++) sam.extend(s[i]-'a');
sam.bt();
sam.dfs(sam.root);
// cout<<sam.fa[1]<<"---"<<endl;
ll fin=sam.query();
printf("%lld\n",fin);
return 0;
}
拓扑序实现代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e7;
typedef long long ll;
char s[maxn];
ll max(ll a,ll b)
{
if(a>b) return a;
else return b;
}
struct SAM
{
int next[maxn][30],fa[maxn],len[maxn];
int c[maxn],id[maxn];
int size[maxn];
int root,tot,last;
int newnode(int l)
{
fa[tot]=0;
for(int i=0;i<30;++i) next[tot][i]=0;
len[tot++]=l; return tot-1;
}
void init()
{
tot=1;
last=root=newnode(0);
}
void extend(int x)
{
int p=last; int cur=newnode(len[p]+1);
size[cur]=1;
while(p!=0&&next[p][x]==0){
next[p][x]=cur; p=fa[p];
}
if(p==0) fa[cur]=root;
else
{
int q=next[p][x];
if(len[q]==len[p]+1) fa[cur]=q;
else
{
int tmp = newnode(len[p]+1);
memcpy(next[tmp],next[q],sizeof(next[q]));
fa[tmp]=fa[q]; fa[q]=fa[cur]=tmp;
while(p!=0&&next[p][x]==q){
next[p][x]=tmp; p=fa[p];
}
}
}
last=cur;
}
void top_sort()
{
for(int i=1;i<=tot;i++) ++c[len[i]];
for(int i=1;i<=tot;i++) c[i]+=c[i-1];
for(int i=1;i<=tot;i++) id[c[len[i]]--]=i;
}
ll query()
{
ll mx=0;
for(int i=tot;i>=1;i--)
{
int z=id[i];
size[fa[z]]+=size[z];
}
for(int i=1;i<=tot;i++)
{
if(size[i]>1) mx=max(size[i]*len[i],mx);
}
return mx;
}
}sam;
int main()
{
scanf("%s",s);
int len=strlen(s);
sam.init();
for(int i=0;i<len;i++) sam.extend(s[i]-'a');
sam.top_sort();
ll fin=sam.query();
printf("%lld\n",fin);
return 0;
}