后缀数组题目选讲


后缀数组题目选讲

复习题:luogu题单

CNOI的题

\(1.\)NOI2015 品酒大会

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

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

因为\(lcp(i,j)=min_{i,我们考虑每个\(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]\)?,这个用主席树维护即可。