ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

后缀数组题目选讲

2022-02-06 10:31:37  阅读:224  来源: 互联网

标签:AA lcp 后缀 选讲 st ge 数组


后缀数组题目选讲

复习题:luogu题单

CNOI的题

\(1.\)NOI2015 品酒大会

题意:\(\forall i∈[0,n)\)求有多少对后缀满足\(lcp \ge i\)​,以及满足条件的两个后缀的权值乘积的最大值。

我们统计出对于\(1...n\)中的每个\(i\)统计一下有多少个\(lcp=i\)再做个后缀和。

因为\(lcp(i,j)=min_{i<k \le j}{heiht_k}\),我们考虑每个\(heiht_k\)能产生的贡献是一段区间。

这个是个单调栈经典问题,求出包含\(heigt_k\)且\(heigt_k\)是最大值的最长区间,然后左右长度相乘即可。

而最值可以用\(st\)表维护,维护最大和次大,最小和次小,因为最大乘积可能由两个负数相乘得出。

\(2.\)​NOI2016 优秀的拆分

计算出\(st_i\)​​​表示以\(i\)​​​​开头的\(AA\)​​​串个数,\(ed_i\)​​​表示以\(i\)​​​结尾的\(AA\)​​​串个数,我们用\(hash\)​​可以\(O(1)\)​​判断一个子串是否为\(AA\)​​串,答案就是\(\sum_{i=1}^{n-1}st_{i+1}\times ed_i\)​​,复杂度\(O(n^2)\)​​,这样就可以拿到\(95pts\)​了​​​​​​,最后5pts打表就行了

考虑如何更快的求出\(st\)​和\(ed\)​,我们枚举 \(AA\)​ 型字符串中\(A\)​的长度\(L\)。每隔\(L\)格放一个关键点,即在\(L,2L,3L...\)位置放关键点。显然,一个长度为\(2L\)的 \(AA\) 型字符串应当经过恰好两个关键点。

设\(lcp(i,j)\)​​为以\(i,j\)​后缀开头的最长公共前缀,\(lcs(i,j)\)​为以\(i,j\)​后缀结束的最长公共后缀,这个可以建正反串后缀数组求出。考虑两个相邻的关键点\(i,j\)​,其中\(i+l=j\)​ ,那么显然当\(lcp(i,j)+lcs(i,j) \ge L\)​时产生了贡献,贡献为\(lcp(i,j)+lcs(i,j)-L+1\)​​

如图红色是\(i,j\)​,蓝色和绿色分别是\(lcp\)​和\(lcs\)​,其中粉色是\(AA\)​串,而这个\(AA\)​串可以一直向后滑动到棕色部分,滑动中经过的串的个数就是\(lcp(i,j)+lcs(i,j)-L+1\)​​,所以我们把红色加粗部分的所有\(st_i\)​加\(1\)​,把绿色加粗部分的所有\(ed_i\)​加\(1\)​,这个用差分实现即可。

复杂度:\(O(nlog_2n+n+\frac n 2+\frac n 3+...+1) \approx O(n \log_2 n)\)​

\(3\)​​.NOI2018 你的名字

题意: 给你一个字符串\(S\), 多次询问给定一个\(T\), 求\(T\)中不在\(S[l...r]\)​中出现的本质不同的子串个数。

老套路,把询问离线,把所有串用一个特殊字符连在一起做后缀数组。

我们按顺序枚举\(T\)​​的每个后缀\(i\)​​,我们求出最大的长度\(L\)​​使得\(\large T[i,i+L-1]\)​​是\(S[l...r]\)​​的子串,那么长度小于\(L\)​​的显然都不满足条件,可以直接计算。这个\(L\)​​是可以二分的,但是我们发现所有的\(i+L-1\)​​这个位置随着\(i\)​​的增大是单调上升的,所以双指针即可。

所以现在只要判断一个\(L\)​是否可行。事实上就是查询\(S[l...r-L+1]\)中是否存在一个\(k\)使得\(lcp(k,i) \ge L\)​,我们先求出满足这个条件的\(k\)在后缀数组上的区间\([Le,Ri]\),然后判断\([Le,Ri]\)中是否存在一个后缀\(k \in [l,r-L+1]\)​,使用主席树查询即可。

用本质不同子串个数减去上述得到的答案即可。

时间复杂度\(O(nlog_2n)\),注意常数。

\(4.\)​[HEOI2016/TJOI2016 字符串

考虑二分答案,二分一个\(L\)只需要判断是否存在一个\(k \in [b-L+1,b]\)使得\(lcp(k,c)=L\),根据上题的做法,先二分出在后缀数组的对应的区间\([Le,Ri]\)​,然后主席树查询即可。

时间复杂度\(O(nlog_2^2n)\)

CF的题

\(5.\)​CF504E

先树剖再按\(dfn\)序建立正反后缀数组,跳重链的时候记录下重链的左右端点,然后拼起来。

每次我们用\(st\)表求出\(lcp(i,j)\)​然后根据长度判断是否还能向扩展。

因为两个点之间重链个数最多只有\(2log_2n\)个,所以我们最多比较\(4log_2n\)次。

这个做法常数很小,最慢的点只要2s,目前我的账号小涵是\(luogu\)​​​的最优解

\(6\).CF666E

老套路,把所有串用一个特殊字符连在一起做后缀数组。

容易发现答案就是对于所有满足\(lcp(pl,k) \ge pr-pl+1\)​中\(col_k \in [l,r]\)​的众数。

我们考虑将询问按 \(pr-pl+1\)​ 从大到小排序,再把\(heiht_i\)​ 也从大到小排序,用两个指针,一个指向当前询问,一个指向当前的\(heiht_i\)​,我们将所有\(height_i \ge pr-pl+1\)​的进行合并操作。也就是把\(i\)​和\(i-1\)​​​​所在的集合合并,用数据结构维护众数即可,很显然用并查集+线段树合并可以很好的维护这个信息。这题的做法和noip2013 货车运输的思想是一样的,所以事实上也可以用\(kruskal\)​​重构树维护众数。

\(7.\)CF1037H

我们枚举答案与\(T\)的\(lcp\)长度\(L\),找出在后缀数组上对应的区间\([l,r]\),因为添加一个字符只会使区间缩小,所以我们用两次二分可以确定新一轮\([l,r]\)。

我们再枚第\(L+1\)​个应该填什么字符,这个字符应满足字典序是大于原字符串的对应位置的字符,按照上述同样的方法求出\([l,r]\)​,然后判断\([l,r]\)​中是否\(\exist~sa_k\in[Le,Ri-L]\)​,这个用主席树维护即可。

标签:AA,lcp,后缀,选讲,st,ge,数组
来源: https://www.cnblogs.com/Xxhdjr/p/15865332.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有