LOJ#6198. 谢特
XXXVI.LOJ#6198. 谢特
SA+笛卡尔树+01trie+启发式合并模板四合一,省选模板练习必备神器
考虑SA后建立笛卡尔树。问题转换为在笛卡尔树的一段区间中(此时该区间内任意两条后缀的LCP长度均为区间中 最小值)任意两条后缀的 最大值。是经典的01trie问题,考虑建立01trie。则,父节点的01trie可以由两个子节点的01trie启发式合并(或者说,因为笛卡尔树也是树,所以是dsu on tree)得到。
复杂度 。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m;
char s[100100];
int sa[100100],rk[100100],ht[100100],buc[100100];
bool mat(int a,int b,int k){
if(ht[a]!=ht[b])return false;
if((a+k<n)!=(b+k<n))return false;
if((a+k<n)&&(b+k<n))return ht[a+k]==ht[b+k];
return true;
}
void SA(){
for(int i=0;i<n;i++)buc[rk[i]=s[i]-'a']++;
for(int i=1;i<m;i++)buc[i]+=buc[i-1];
for(int i=n-1;i>=0;i--)sa[--buc[rk[i]]]=i;
for(int k=1;k<n;k<<=1){
int num=0;
for(int i=n-k;i<n;i++)ht[num++]=i;
for(int i=0;i<n;i++)if(sa[i]>=k)ht[num++]=sa[i]-k;
for(int i=0;i<=m;i++)buc[i]=0;
for(int i=0;i<n;i++)buc[rk[ht[i]]]++;
for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
for(int i=n-1;i>=0;i--)sa[--buc[rk[ht[i]]]]=ht[i],ht[i]=0;
swap(rk,ht),rk[sa[0]]=num=0;
for(int i=1;i<n;i++)rk[sa[i]]=mat(sa[i],sa[i-1],k)?num:++num;
if(num>=n-1)break;
m=num;
}
for(int i=0;i<n;i++)ht[i]=0;
for(int i=0,k=0;i<n;i++){
if(!rk[i])continue;
if(k)k--;
int j=sa[rk[i]-1];
while(i+k<n&&j+k<n&&s[i+k]==s[j+k])k++;
ht[rk[i]]=k;
}
}
int L[100100],R[100100],ls[100100],rs[100100],stk[100100],tp,res;
int a[100100],rt[100100],cnt,bin[10010000];
const int LG=17;
struct Trie{int ch[2];}t[10010000];
int newnode(){return tp?bin[tp--]:++cnt;}
void ins(int &x,int y,int pos){
if(!x)x=newnode();
if(pos==-1)return;
ins(t[x].ch[(y>>pos)&1],y,pos-1);
}
void erase(int &x){if(!x)return;bin[++tp]=x,erase(t[x].ch[0]),erase(t[x].ch[1]),x=0;}
int ask(int x,int y,int pos){
if(pos==-1)return 0;
if(t[x].ch[!((y>>pos)&1)])return ask(t[x].ch[!((y>>pos)&1)],y,pos-1)+(1<<pos);
else return ask(t[x].ch[(y>>pos)&1],y,pos-1);
}
int dfs(int x){
int ret=0;
if(ls[x])ret=max(ret,dfs(ls[x]));
if(rs[x])ret=max(ret,dfs(rs[x]));
if(x-L[x]>R[x]-x+1){
if(ls[x])rt[x]=rt[ls[x]];else ins(rt[x],a[sa[L[x]]],LG-1);
if(rs[x])erase(rt[rs[x]]);
for(int i=x;i<=R[x];i++)ret=max(ret,ask(rt[x],a[sa[i]],LG-1)),ins(rt[x],a[sa[i]],LG-1);
}else{
if(rs[x])rt[x]=rt[rs[x]];else ins(rt[x],a[sa[R[x]]],LG-1);
if(ls[x])erase(rt[ls[x]]);
for(int i=L[x];i<x;i++)ret=max(ret,ask(rt[x],a[sa[i]],LG-1)),ins(rt[x],a[sa[i]],LG-1);
}
res=max(res,ret+ht[x]);
return ret;
}
int main(){
scanf("%d%s",&n,s),m=26,SA();
// for(int i=0;i<n;i++)printf("%d ",i);puts("");
// for(int i=0;i<n;i++)printf("%d ",sa[i]);puts("");
// for(int i=0;i<n;i++)printf("%d ",rk[i]);puts("");
// for(int i=0;i<n;i++)printf("%d ",ht[i]);puts("");
// for(int i=0;i<n;i++)printf("%s\n",s+sa[i]);
for(int i=1;i<n;i++){
while(tp&&ht[stk[tp]]>ht[i])R[ls[i]=stk[tp--]]=i-1;
if(tp)L[i]=stk[tp];else L[i]=0;
rs[stk[tp]]=i,stk[++tp]=i;
}
while(tp)R[stk[tp--]]=n-1;
for(int i=0;i<n;i++)scanf("%d",&a[i]);
// for(int i=1;i<n;i++)printf("%d[%d,%d](%d,%d)\n",i,L[i],R[i],ls[i],rs[i]);
dfs(stk[1]),printf("%d\n",res);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?