P7409 SvT 题解
前置知识
- 后缀数组;
- height 数组。
题目大意
给出一个字符串
解题思路
首先对于
考虑
- 对于排序后的两个后缀
和 ,有 ;
那么,设每次查询的后缀,排序后起始位置为
但是,显然我们不能枚举两个后缀然后计算它们的 LCP。这个时候我们就需要使用单调栈。我们可以令
时间复杂度
AC 代码
#include<stdio.h>
#include<string.h>
#include <valarray>
#define int long long
#define N 1000005
#define Mode 23333333333333333
int cnt[N],SA[N],pos[N];
int rank[N],height[N];
char s[N];int n,q;
inline void GetSA(){int m='z';
memset(cnt,0,sizeof(cnt));
for(register int i=1;i<=n;++i)
++cnt[rank[i]=s[i]];
for(register int i=1;i<=m;++i)
cnt[i]+=cnt[i-1];
for(register int i=n;i;--i)
SA[cnt[rank[i]]--]=i;
for(register int k=1;k<=n;k<<=1){
int num=0;
for(register int i=n-k+1;i<=n;++i)
pos[++num]=i;
for(register int i=1;i<=n;++i)
if(SA[i]>k) pos[++num]=SA[i]-k;
memset(cnt,0,sizeof(cnt));
for(register int i=1;i<=n;++i)
++cnt[rank[i]];
for(register int i=1;i<=m;++i)
cnt[i]+=cnt[i-1];
for(register int i=n;i;--i)
SA[cnt[rank[pos[i]]]--]=pos[i],pos[i]=0;
std::swap(rank,pos),rank[SA[1]]=1,num=1;
for(register int i=2;i<=n;++i)
rank[SA[i]]=
num+=pos[SA[i]]^pos[SA[i-1]]
||pos[SA[i]+k]^pos[SA[i-1]+k];
if(num==n) break;m=num;
}
}
inline void GetHeight(){
for(register int i=1;i<=n;++i)
rank[SA[i]]=i;
for(register int i=1,k=0;i<=n;++i){
if(rank[i]==1) continue;if(k)--k;
for(register int j=SA[rank[i]-1];
i+k<=n&&j+k<=n&&s[i+k]==s[j+k];++k);
height[rank[i]]=k;
}
}
int mint[N][30],lg[N];
inline void InitST(){lg[0]=-1;
for(register int i=1;i<=n;++i){
lg[i]=lg[i>>1]+1;
mint[i][0]=height[i];
}for(register int j=1;j<=lg[n];++j)
for(register int i=1;i+(1<<j)<=n+1;++i)
mint[i][j]=std::min(mint[i][j-1],
mint[i+(1<<(j-1))][j-1]);
}
inline int GetMint(int l,int r){
if(l>r) std::swap(l,r);
int k=lg[r-l+1];
return std::min(mint[l][k],
mint[r-(1<<k)+1][k]);
}
int sta[N],tail,l[N],r[N];
int p[N],h[N],ans,ce,all;
inline void Query(){
scanf("%lld",&all);
for(register int i=1;i<=all;++i)
scanf("%lld",&p[i]);
for(register int i=1;i<=all;++i)
p[i]=rank[p[i]];
std::sort(p+1,p+all+1);
ce=std::unique(p+1,p+all+1)-p-1;
for(register int i=2;i<=ce;++i)
h[i]=GetMint(p[i-1]+1,p[i]);
h[1]=0;sta[1]=1;tail=1;ans=0;
for(register int i=2;i<=ce;++i){
while(tail&&h[sta[tail]]>h[i])
r[sta[tail--]]=i;
l[i]=sta[tail];sta[++tail]=i;
}while(tail) r[sta[tail--]]=ce+1;
for(register int i=2;i<=ce;++i){
ans+=(r[i]-i)*(i-l[i])
*h[i];
ans=ans;
}printf("%lld\n",ans);
}
signed main(){
scanf("%lld%lld",&n,&q);
scanf("%s",s+1);
GetSA();GetHeight();
InitST();
while(q--) Query();
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下