java tire树_谢特——后缀数组+tire 树(示例代码)

该博客介绍了如何利用后缀数组和Trie树解决求字符串后缀之间贡献最大值的问题。通过建立Trie树进行贪心更新,结合后缀数组计算LCP,实现了$O(n log n + n log^2 n)$的时间复杂度解题方案。
摘要由CSDN通过智能技术生成

题目

【题目描述】

由于你成功地在 $ ext{1 s} $ 内算出了上一题的答案,英雄们很高兴并邀请你加入了他们的游戏。然而进入游戏之后你才发现,英雄们打的游戏和你想象的并不一样……

英雄们打的游戏是这样的:首先系统会产生(**注意不一定是随机产生**)一个字符串,然后每个英雄就会开始根据自己分到的任务计算这个字符串的某些特征,谁先算出自己的答案谁就是胜者。

由于打游戏的英雄比较多,因此英雄们分到的任务也就可能很奇怪。比如你分到的这个任务就是这样:

定义这个字符串以第 $ i $ 个字符开头的后缀为后缀 $ i $ (编号从 $ 1 $ 开始),每个后缀 $ i $ 都有一个权值 $ w_i $ ,同时定义两个后缀 $ i,j $ ($ i

e j $) 的贡献为它们的最长公共前缀长度加上它们权值的异或和,也就是 $ mathrm{LCP}(i,j)+(w_i mathbin{ext{xor}} w_j) $ 。而你的任务就是,求出这个字符串的所有后缀两两之间贡献的最大值。

【输入格式】

第一行一个正整数 $ n $,表示字符串的长度。

第二行一个仅包含小写英文字母的字符串,即系统产生的字符串。

第三行 $ n $ 个非负整数 $ w_i $,分别表示后缀 $ 1 $ ~ $ n $ 的权值。

【输出格式】

一行一个整数表示答案。

【样例输入】

7

acbabac

0 1 5 6 4 2 3

【样例输出】

7

【样例解释】

后缀 $ 1 $ 和后缀 $ 4 $ 的贡献是 $ 1+(0;ext{xor};6)=7 $ ,不难验证它们的贡献确实是所有可能的贡献中最大的。

【数据范围与提示】

对于 $ 30\% $ 的数据,$ nle 5imes 10^3 $;

对于另 $ 30\% $ 的数据,保证字符串是随机生成的;

对于另 $ 10\% $ 的数据,$ w_i=0 $;

对于另 $ 10\% $ 的数据,$ w_ile 1 $;

对于 $ 100\% $ 的数据,$ nle 10^5 $,$ w_i< n $ 。

题解

求任意两个后缀的 LCP 很容易想到后缀数组

记排序后的两个相邻后缀 $ i-1,i $ 的 LCP 为 $ height[i] $

那么任意的两个后缀 $ i,j $ 的 LCP 为 $ min_{k=i}^{j}height[k] $

至于求 $W$ 的异或值考虑在 tire 树上贪心

当 $ height[i] $ 为 $[l,r] $ 的最小值时才会对该区间有影响,那么考虑如何用 $ height[i] $ 来更新答案

将 $ height[i] $ 从大到小排序后,合并 $ P_i $ 和 $ P_{i-1} $ 属于的两个区间 $ [L_{P_i},R_{p_i}] $ 和 $ [L_{P_{i-1}},R_{P_{i-1}}] $,此时保证 $ height[i] $ 为两个区间中的最小值(因为比 $ i $ 大的已经合并了)

然后在 tire 树上启发式合并两个区间即可,贪心选取答案

时间效率:$ O(n log n+n log^2n)$

至于 SA 的排序可以用倍增法或者二分哈希都可以(也就多一个 $ log $,反正启发式合并也要 $ log^2 $)

为什么我一点都没有感觉到套路,可能是题写太少了

代码

lazy.gif

lazy.gif

1 #include

2 #define LL long long

3 #define _(d) while(d(isdigit(ch=getchar())))

4 using namespacestd;5 intR(){6 int x;bool f=1;char ch;_(!)if(ch==‘-‘)f=0;x=ch^48;7 _()x=(x<<3)+(x<<1)+(ch^48);return f?x:-x;}8 const int N=2e5+5;9 intn,m,w[N],p[N],ht[N],rak[N],tp[N],sa[N],tax[N],ans;10 charch[N];11 voidQsort(){12 for(int i=0;i<=m;i++)tax[i]=0;13 for(int i=1;i<=n;i++)tax[rak[tp[i]]]++;14 for(int i=1;i<=m;i++)tax[i]+=tax[i-1];15 for(int i=n;i>=0;i--)sa[tax[rak[tp[i]]]--]=tp[i];16 }17 voidSA(){18 m=26,Qsort();19 for(int l=1,p=0;l<=n;l<<=1){20 for(int i=n-l+1;i<=n;i++)tp[++p]=i;21 for(int i=1;i<=n;i++)if(sa[i]>l)tp[++p]=sa[i]-l;22 Qsort(),swap(rak,tp);23 rak[sa[1]]=p=1;24 for(int i=2;i<=n;i++)25 rak[sa[i]]=(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+l]==tp[sa[i]+l])?p:++p;26 if(p>n)break;27 m=p+1,p=0;28 }29 int k=0;30 for(int i=1,j;i<=n;i++){31 j=sa[rak[i]-1];32 if(k)k--;33 while(ch[j+k]==ch[i+k])k++;34 ht[rak[i]]=k;35 }36 }37 bool cmp(int a,int b){return ht[a]>ht[b];}38 int li[N],ri[N],fa[N],rt[N],tot,tr[N*50][2];39 int query(int k,int dep,intval){40 if(!~dep)return 0;41 if(tr[k][((val>>dep)&1)^1])42 return (1<>dep)&1)^1],dep-1,val);43 else return query(tr[k][(val>>dep)&1],dep-1,val);44 }45 void insert(int &k,int dep,intval){46 if(!k)k=++tot;47 if(~dep)insert(tr[k][(val>>dep)&1],dep-1,val);48 }49 int merge(int x,inty){50 int res=0;51 if(ri[x]-li[x]

View Code

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值