hiho#1449 重复旋律6 求长度为k的串最大次数 后缀自动机

时间:2023-03-09 22:50:34
hiho#1449 重复旋律6  求长度为k的串最大次数     后缀自动机

题目传送门

题目大意:求长度为k的串的最大次数,把k从1到length的所有答案全部输出。

思路:

  这道题放在$SAM$里就是求长度$k$对应的所有$right$集中最大的大小。

  我们以$aabab$这个串距离,称作$S$串。

  首先我们要求出每一个状态对应的right集大小,S中的aab和ab此时并不在一个模式下,但他们属于一个right集,而ab是aab的父串,所以我们用拓扑排序的方式得到right集,这里的更新操作是$+$,因为ab对应的模式此时并没有包含aab中的ab。

  而得到right后,我们会发现在SAM上并没有一个单独的模式对应b,因为b被包含在了ab这个模式里面,所以我们也要用长度为2的答案来更新长度为1的答案,这里是取max,因为有可能是重复的,比如$abab$这样的串。

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=;
char s[maxn];
int len[maxn<<],ch[maxn<<][],fa[maxn<<],tot=,root=,last=,siz,r[maxn<<];
int a[maxn<<],c[maxn<<],ans[maxn<<];
void extend(int x){
int now=++tot,pre=last;
r[now]=;
last=now,len[now]=len[pre]+;
while( pre && !ch[pre][x]){
ch[pre][x]=now;
pre=fa[pre];
}
if(!pre)fa[now]=root;
else{
int q = ch[pre][x];
if(len[q]==len[pre]+)fa[now]=q;
else {
int nows=++tot;
memcpy(ch[nows],ch[q],sizeof(ch[q]));
len[nows]=len[pre]+;
fa[nows]=fa[q];
fa[q]=fa[now]=nows;
while(pre&&ch[pre][x]==q){
ch[pre][x]=nows;
pre=fa[pre];
}
}
}
}
void topSort(){
for(int i=;i<=tot;i++)c[len[i]]++;
for(int i=;i<=tot;i++)c[i]+=c[i-];
for(int i=tot;i>;i--)a[c[len[i]]--]=i;
for(int i=tot;i>;i--)r[fa[a[i]]]+=r[a[i]];
}
int main(){
scanf("%s",s);
siz=strlen(s);
for(int i=;i<siz;i++)
{
int p=s[i]-'a';
extend(p);
}
topSort();
for(int i=;i<=tot;i++)ans[len[i]]=max(ans[len[i]],r[i]);
for(int i=siz-;i>;i--)ans[i]=max(ans[i+],ans[i]);
for(int i=;i<=siz;i++)printf("%d\n",ans[i]); }