spoj 8222 NSUBSTR 求长度为x的子串中出现次数最大值 SAM
题目大意
给一个字符串S
令F(x)表示S的所有长度为x的子串中
出现次数的最大值。
求F(1)..F(Length(S))
分析
一个节点\(x\)的长度有\(~~(max(fa),max(x)]\)
出现次数为\(|Right(x)|\)
则\((max(fa),max(x)]\)的出现次数都\(\ge |Right(x)|\)
做法
注意到对于一个点\(x\)的祖先链,长度是[1..max(fa)]
而且他们的\(|Right()|\)都\(\ge |Right(x)|\)
所有更新时对于\(x\)我们直接更新\([1...max(x)]是可以的\)
只更新到\(max(x)\),像后缀和那样求答案就好了
solution
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <algorithm>
using namespace std;
const int M=524288;
int go[M][26];
int fa[M];
int stp[M];
int sum[M];
int pos[M];
int right[M];
int last,tot;
char s[M];
int mx[M];
int n;
int newnode(int ss){
stp[++tot]=ss;
return tot;
}
int ext(int p,int q,int d){
int nq=newnode(stp[p]+1);
fa[nq]=fa[q];
fa[q]=nq;
memcpy(go[nq],go[q],sizeof(go[q]));
for(;p&&go[p][d]==q;p=fa[p]) go[p][d]=nq;
return nq;
}
int sam(int p,int d){
int np=go[p][d];
if(np) return (stp[p]+1==stp[np]) ? np : ext(p,np,d);
else{
np=newnode(stp[p]+1);
right[np]=1;
for(;p&&!go[p][d];p=fa[p]) go[p][d]=np;
if(!p) fa[np]=1;
else{
int q=go[p][d];
fa[np]= (stp[p]+1==stp[q]) ? q : ext(p,q,d);
}
}
return np;
}
int main(){
int i;
scanf("%s",s+1);
n=strlen(s+1);
last=tot=1;
for(i=1;i<=n;i++) last=sam(last,s[i]-'a');
for(i=1;i<=tot;i++) sum[stp[i]]++;
for(i=1;i<=n;i++) sum[i]+=sum[i-1];
for(i=1;i<=tot;i++) pos[sum[stp[i]]--]=i;
for(i=tot;i>0;i--) right[fa[pos[i]]]+=right[pos[i]];
for(i=1;i<=tot;i++) mx[stp[i]]=max(mx[stp[i]],right[i]);
for(i=n;i>0;i--) mx[i]=max(mx[i],mx[i+1]);
for(i=1;i<=n;i++) printf("%d\n",mx[i]);
return 0;
}