后缀自动机的应用
后缀自动机的原理就不在赘述了,这里主要介绍它的应用。
板子:
struct node{
int c[26],len,fa;
} a[maxn];
void build(int x){
int p=las;int np=las=++tot;
a[np].len=a[p].len+1;
for(;p&&!a[p].c[x];p=a[p].fa)
a[p].c[x]=np;
if(!p)
a[np].fa=1;
else{
int q=a[p].c[x];
if(a[q].len==a[p].len+1)
a[np].fa=q;//don't write 'a[p].fa=q'!
else{
int nq=++tot;
a[nq]=a[q];
a[nq].len=a[p].len+1;//don't forget!
a[q].fa=a[np].fa=nq;
for(;p&&a[p].c[x]==q;p=a[p].fa)
a[p].c[x]=nq;
}
}
siz[np]=1;
}
一、统计子串出现次数
一个子串的出现次数为它在母树中的儿子的数量,在母树上
for(int i=0;i<s.size();++i)
build(s[i]-'a');
for(int i=1;i<=tot;++i)
++c[a[i].len];
for(int i=1;i<=tot;++i)
c[i]+=c[i-1];
for(int i=1;i<=tot;++i)
id[c[a[i].len]--]=i;
for(int i=tot;i>=1;--i){
int p=id[i];
siz[a[p].fa]+=siz[p];
if(siz[p]>1)
ans=max(ans,1ll*siz[p]*a[p].len);
}
printf("%lld\n",ans);
return 0;
例题:P5341 [TJOI2019] 甲苯先生和大中锋的字符串
void work(){
tot=las=1;ans=0;
cin>>s;scanf("%d",&n);
for(int i=0;i<s.size();++i)
build(s[i]-'a');
for(int i=1;i<=tot;++i)
++c[a[i].len];
for(int i=1;i<=tot;++i)
c[i]+=c[i-1];
for(int i=1;i<=tot;++i)
id[c[a[i].len]--]=i;
for(int i=tot;i>=1;--i){
int p=id[i];
siz[a[p].fa]+=siz[p];
if(siz[p]==n)//差分思想,令f[i]表示出现次数为n,长度为i的字符串个数,则sum[i]=f[i]-f[i+1]
++sum[a[p].len],--sum[a[a[p].fa].len];
}
for(int i=s.size();i>=1;--i){
sum[i]+=sum[i+1];//得到上述的f[i]
if(sum[ans]<sum[i])
ans=i;
}
if(ans)
printf("%d\n",ans);
else
printf("-1\n");
for(int i=0;i<=tot;++i){
c[i]=sum[i]=0;
siz[i]=id[i]=a[i].fa=a[i].len=0;
for(int j=0;j<26;++j)
a[i].c[j]=0;
}
}
二、统计不同子串个数
后缀自动机有一个性质:从根节点开始跑,每一条路径代表一个子串。
又因为后缀自动机是个
例题:P2408 不同子串个数
signed main(){
scanf("%lld",&n);
cin>>s;
len=s.size();
for(int i=0;i<len;++i)
add(s[i]-'a');
for(int i=1;i<=tot;++i)
++cnt[a[i].len];
for(int i=1;i<=len;++i)
cnt[i]+=cnt[i-1];
for(int i=1;i<=tot;++i)
d[cnt[a[i].len]--]=i;
for(int i=tot;i>=1;--i)
siz[a[d[i]].fa]+=siz[d[i]];
for(int i=1;i<=tot;++i)
sum[i]=siz[i]=1;
sum[1]=siz[1]=0;
for(int i=tot;i>=1;--i)
for(int j=0;j<26;++j)
sum[d[i]]+=sum[a[d[i]].ch[j]];
printf("%lld\n",sum[1]);
return 0;
}
三、字典序第 小子串
令
void dfs(int p,int k){
if(k<=siz[p])
return ;
k-=siz[p];
for(int i=0;i<26;++i){
int to=a[p].ch[i];
if(!to)
continue;
if(k>sum[to])
k-=sum[to];
else{
printf("%c",i+'a'),dfs(to,k);
return ;
}
}
}
int main(){
cin>>s;
scanf("%d%d",&t,&k);
len=s.size();
for(int i=0;i<len;++i)
add(s[i]-'a');
for(int i=1;i<=tot;++i)
++cnt[a[i].len];
for(int i=1;i<=len;++i)
cnt[i]+=cnt[i-1];
for(int i=1;i<=tot;++i)
d[cnt[a[i].len]--]=i;
for(int i=tot;i>=1;--i)
siz[a[d[i]].fa]+=siz[d[i]];
for(int i=1;i<=tot;++i)
if(t==0)
sum[i]=siz[i]=1;
else
sum[i]=siz[i];
sum[1]=siz[1]=0;
for(int i=tot;i>=1;--i)
for(int j=0;j<26;++j)
sum[d[i]]+=sum[a[d[i]].ch[j]];
if(sum[1]>=k)
dfs(1,k);
else
printf("-1\n");
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探