首页 > 编程语言 > 详细

「后缀数组」

时间:2019-12-25 09:23:09      阅读:85      评论:0      收藏:0      [点我收藏+]

前言

做的题比较水,写总结也请见谅。

应用

目前做的题都是比较套路的题,一些应用会直接在题解里涉及到。

在用于统计子串时很管用,因为一个子串必定唯一对应一个后缀的前缀。

可以在「一对多」的时候使用。AC自动机是「多对一」。

其实是我忘了有啥了。

联系

后缀树

后缀自动机

AC自动机

个人认为AC自动机的「fail树」与后缀数组的不同就是「fail树」是表示的所有前缀的后缀。

例题

A. Sandy的卡片

改了26次的题,很有细节。

首先将题目相同的定义转化为差分后串相同。

然后题目就相当于求一些字符串的最长相同串。

做法就是把这些字符串用特殊字符隔开,跑后缀数组处理出「height」。

二分答案,就可以把以排名为下标的数组分成若干个「height」>=mid的区间。

判断是否存在一个区间满足有所有串的点即可。

需要注意「height」是i与i-1的最长公共前缀,所以这个区间应该包含前面的那个点的串种类。

B. 喵星球上的点名

首先将姓名串和询问串串在一起跑SA。

每一个询问串,可以二分出来左右端点「L,R」满足lcp==询问串长度,显然这是有二分性的。

答案就是区间[L,R]的不同姓名串的个数,可以莫队做或者离线BIT做,或者主席树数颜色做。

对于第二问,可以BIT做,也可以差分做。

差分:

在莫队过程中,对于新加入的一种新颜色,把这种颜色加入$m-now+1$的贡献,不妨假设这种颜色以后$m-now+1$次都出现。

对于离开的一种颜色,减去$m-now+1$的贡献。

容易发现这种差分是正确的。

BIT:

第一问是求每个区间不同颜色的个数。

第二问是求一种颜色被不同区间的覆盖数量。

第二问:

将区间按l升序,枚举当前位置i,左端点在BIT上+1,右端点就在相应的左端点处-1,统计这个点的答案就是$sum(i)-sum(pre[i])$

$pre[i]$是上一个该颜色的位置。

思考为什么是对的。

考虑用每个位置为这种颜色贡献答案,那么答案就是$\sum sum(i)$

可以说该位置能贡献的答案就是上一个位置与这个位置之间的区间个数。

再思考一下左端点在BIT上+1,右端点就在相应的左端点处-1的过程,就是处理出当前点i被包含的区间个数。

再减去$sum(pre[i])$就是答案。

C. 字符串

大意:求一段区间的子串与一个串的lcp最大值。

二分lcp长度,二分出如A题的区间[l,r],里面如果有[a,b]内的串并且长度满足条件这个lcp长度就合法。

主席树没打,直接枚举[l,r]每个位置就可以过。

D. 差异

单调栈维护每个后缀的height作为最小值的区间,累加区间的贡献就是答案。

E. 相似子串

先跑SA,本质不同的串排名就是$\sum\limits_{i=1}^n n-sa_i+1-height_i$

找到第i个子串,第j个子串的左右端点,剩下的就是ST表区间最小值求lcp。

求lcp时如果是自己跟自己搞,要特判返回$n-sa_i+1$,否则把左端点+1再RMQ。

还要把串反着来一遍,就是开两个SA的结构体,好像直接反着插进原串后面也行(没有考证)。

F. 品酒大会

首先「r相似」可以变成「0~r」相似,其中「1~r相似」是题目给出的,「0相似」也能变是因为无论是「x相似」「y相似」都等价于一个点对的关系,而题目里要求的「0相似」就是所有点对的关系。

当然你特判也没有什么问题 的说。

问题变成求两个后缀的lcp,然后在统计答案时取后缀和即可。

因为有负数,

询问最大值就相当于去以该点为中间点$[l_i,i-1]$与$[i,r_i]$的最小值之积与最大值之积取max。

单调栈维护每个后缀的height作为最小值的区间,累加区间的贡献就是答案。

「不觉得这句话好像出现过吗?」


 

A. 外星联络

挺水的一道题。

就是找到本质不同的子串的出现次数,跑一遍SA之后按排名遍历一遍。

它又支持$O(n^2)$有height的直接继承上个子串每个位置的真正排名累计答案,新的串直接新建排名累计答案。

最后答案就是出现次数大于1的子串,按排名输出。

B. 跳蚤

一道没颓题解「全靠$ak_t$」的好题。

我的做法:

首先二分最后答案,但字符串无法二分,考虑最后答案一定出现在原串中,那就二分答案在子串中的排名。

排名在mid后面的子串都不能出现,就是必须砍,我们的目的就是判断把后面的串都砍掉的最小花费与k-1的关系。

根据贪心思想,一定是砍的越多越好,因此就不用二分k了,就算二分它也一定是单增的。

还是用SA求出mid在本质不同的串中的排名,之后算出这个串的起点为$i$,长度为$j$。

以下说的$i$,都是指的是排名为$i$的后缀。

那么起点为$i$,长度为$j+1$~$n-sa_i+1$都需要被砍至少一刀咯,也就是如果把点$i$想象成$i$与$i+1$直接的空隙。

那么最优策略就是在$i$~$i+j-1$之间砍一刀,因为这样会截去所有起点为i的不合法串。

同理对于起点为$i+1$~$n$的串,只要满足截去所有起点为$i$~$n$的不合法串,并且砍的次数<=$k-1$,那就说明这个mid合法。

那么问题转化问到底对于每个起点,最优策略的区间到底是哪里。

还是那个$j$,我们发现对于后面的起点k,以它为起点,不用砍的串就是长度<=「$j$与$i+1$~$k$的height取min」,这个东西不叫lcp了,是我yy出来,感觉好像是对的就打了出来。

可以理解为$k$存在的「等价$i$为起点的合法子串」。

这样就把问题再次转移成了「给定m个区间,有p次覆盖的机会,求能否至少覆盖每个区间一次」的问题。

$真·不能Ak_t$告诉我可以用贪心的思路:

将区间拆成$l$$r$两个位置,放入相应的vector里这个区间的下标。

枚举$1~n$将左端点全部加入栈里,碰到一个没有访问过的右端点就直接把栈里左端点对应的区间下标标记为访问过,并cnt++。

这样做为什么是最小的,感性理解一下吧,每次只在最不能省去的地方砍,一定是优的。

还有一个点就是$n$点对应的间隙是不用保证的,因为首尾自动砍断 的说。

题解的做法:

前面二分不变。

然后在原序列上倒着一个字符一个字符的添加,如果说加完这个字符,这个串的排名就>mid的话,那么这个字符与之前的串之间的间隙就必须砍一刀。

正确性,显然。最优性,因为每次都是在不得不断情况之前才断,因此一定是最优的。

砍的次数<=$k-1$,那就说明这个mid合法。

skyh的做法:

前面二分不变。

接着维护一个$mn$表示需要砍的最小位置,每次更新$mn=min(mn,min(len,lcp(st,j))$

容易发现这和我的做法等价,但是更简洁了。

D. 股市的预测

E. SvT

分治,lcp。

考虑分治时候每个点只会被作为最小值RMQ一次,所以总复杂度$O(nlog)$。

「后缀数组」

原文:https://www.cnblogs.com/hzoi2018-xuefeng/p/12093115.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!