SAM 做题记录
pigeon->gugugu();
建出 SAM 后 在 parent tree 上 dp,对每个节点算它子树里面叶子节点个数,然后根据题意统计答案即可。
Code
#include<bits/stdc++.h>
using namespace std;
const int N=1000005*3;
struct node{int len,link,to[30];};
int n,hd[N],tot,sz[N];
long long ans;
char s[N];
struct edge{int t,nxt;}es[N<<1];
void add(int u,int v){es[++tot]=(edge){v,hd[u]};hd[u]=tot;}
namespace SAM{
node state[N];
int rt=1,last=1,tot=1;
void extend(int ch){
int cur=++tot,p,q;
sz[cur]=1;
state[cur].len=state[last].len+1;
for(p=last;p&&!state[p].to[ch];p=state[p].link)state[p].to[ch]=cur;
if(!p)state[cur].link=rt;
else{
q=state[p].to[ch];
if(state[p].len+1==state[q].len)state[cur].link=q;
else{
int clone=++tot;
state[clone]=state[q];
state[clone].len=state[p].len+1;
state[cur].link=state[q].link=clone;
for(;p&&state[p].to[ch]==q;p=state[p].link)state[p].to[ch]=clone;
}
}
last=cur;
}
}
void dfs(int u){
for(int i=hd[u];i;i=es[i].nxt)dfs(es[i].t),sz[u]+=sz[es[i].t];
if(sz[u]!=1)ans=max(ans,1LL*sz[u]*SAM::state[u].len);
}
int main(){
scanf("%s",s);n=strlen(s);
for(int i=0;i<n;i++)SAM::extend(s[i]-'a');
for(int i=2;i<=SAM::tot;i++)add(SAM::state[i].link,i);
dfs(SAM::rt);
printf("%lld\n",ans);
return 0;
}
两倍长建 SAM,每次走最小的边,走 \(n\) 次即可。
Code
#include<bits/stdc++.h>
using namespace std;
const int N=300005*3;
struct node{int len,link;map<int,int>to;};
struct Suffix_Automation{
node state[N];
int rt=1,last=1,tot=1;
void extend(int ch){
int cur=++tot,p,q;
state[cur].len=state[last].len+1;
for(p=last;p&&!state[p].to[ch];p=state[p].link)state[p].to[ch]=cur;
if(!p)state[cur].link=rt;
else{
q=state[p].to[ch];
if(state[p].len+1==state[q].len)state[cur].link=q;
else{
int clone=++tot;
state[clone]=state[q];
state[clone].len=state[p].len+1;
state[cur].link=state[q].link=clone;
for(;p&&state[p].to[ch]==q;p=state[p].link)state[p].to[ch]=clone;
}
}
last=cur;
}
}SAM;
int n,a[N];
map<int,int>::iterator it;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),SAM.extend(a[i]);
for(int i=1;i<=n;i++)SAM.extend(a[i]);
for(int i=1,nw=SAM.rt;i<=n;i++)it=SAM.state[nw].to.begin(),printf("%d ",it->first),nw=it->second;
return 0;
}
这题貌似和第一题很像,其实处理方法不一样。
根据 \(\operatorname{link}\) 的定义,状态 \(v\) 对应的字符串个数为 \(\operatorname{len}(i)-\operatorname{len}(\operatorname{link}(i))\),那么每次加字符的时候更新就可以了。
Code
#include<bits/stdc++.h>
using namespace std;
const int N=300005*3;
struct node{int len,link;map<int,int>to;};
struct Suffix_Automation{
node state[N];
int rt=1,last=1,tot=1;
long long ans;
void extend(int ch){
int cur=++tot,p,q;
state[cur].len=state[last].len+1;
for(p=last;p&&!state[p].to[ch];p=state[p].link)state[p].to[ch]=cur;
if(!p)state[cur].link=rt;
else{
q=state[p].to[ch];
if(state[p].len+1==state[q].len)state[cur].link=q;
else{
int clone=++tot;
state[clone]=state[q];
state[clone].len=state[p].len+1;
ans-=state[q].len-state[state[q].link].len;
state[cur].link=state[q].link=clone;
ans+=state[q].len-state[state[q].link].len;
ans+=state[clone].len-state[state[clone].link].len;
for(;p&&state[p].to[ch]==q;p=state[p].link)state[p].to[ch]=clone;
}
}
ans+=state[cur].len-state[state[cur].link].len;
last=cur;
}
}SAM;
int n,a[N];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),SAM.extend(a[i]),printf("%lld\n",SAM.ans);
return 0;
}