zoukankan      html  css  js  c++  java
  • 「后缀数组」

    前言

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

    应用

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

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

    可以在「一对多」的时候使用。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,本质不同的串排名就是$sumlimits_{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. 股市的预测

    一道我认为比B题还难的题。从7:00打到10:30,还全程颓题解。

    题目描述

    墨墨的妈妈热爱炒股,她要求墨墨为她编写一个软件,预测某只股票未来的走势。股票折线图是研究股票的必备工具,它通过一张时间与股票的价位的函数图像清晰地展示了股票的走势情况。经过长时间的观测,墨墨发现很多股票都有如下的规律:之前的走势很可能在短时间内重现!如图可以看到这只股票A部分的股价和C部分的股价的走势如出一辙。通过这个观测,墨墨认为他可能找到了一个预测股票未来走势的方法。进一步的研究可是难住了墨墨,他本想试图统计B部分的长度与发生这种情况的概率关系,不过由于数据量过于庞大,依赖人脑的力量难以完成,于是墨墨找到了善于编程的你,请你帮他找一找给定重现的间隔(B部分的长度),有多少个时间段满足首尾部分的走势完全相同呢?当然,首尾部分的长度不能为零。
    image

    输入格式

    第一行包含两个整数N、M,分别表示需要统计的总时间以及重现的间隔(B部分的长度)。
    接下来N行,每行一个整数,代表每一个时间点的股价。
    4≤N≤50000 1≤M≤10 M≤N 所有出现的整数均不超过32位含符号整数。

    输出格式

    输出一个整数,表示满足条件的时间段的个数

    样例

    样例输入

    12 4
    1 2 3 4 8 9 1 2 3 4 8 9
    

    样例输出

    6
    

    样例说明

    6个时间段分别是:3-9、2-10、2-8、1-9、3-11、4-12。

    有了A题的思路,我们可以用差分来解决走势相同。

    刚开始的错解是枚举M串的位置,向左向右二分出最大匹配长度直接加就行,但是这不具有二分性。「然而瑙殘哥还是打出了75分的傲人成绩」

    正解:

    枚举形如ABA串的A长度「len」。

    把原串按len分块,考虑每块的第一个「i」与「j=i+len+m」构成的答案有多少。

    向左向右拓展$ij$的最大匹配长度,此时还要长度$<=len$以免重复算。

    如果相加大于$len$,就可以构成$sum-len+1$个不同的合法的串。

    正确性显然,考虑为啥不重不漏。

    不重:每个合法的串都一定包括当前块的$i$而不包括前后块的$i‘$(因为限制了长度$<=len$),所以每个合法的串都不会重复。

    不漏:每个合法的串都一定会包含一个且仅一个块的$i$,也就是说如果有漏的串它也一定包含某个$i$,而我们枚举了每个$i$的合法串情况,因此一定不漏。

    记得求$lcp$时因为是由原串的$i$转为了$rk_i$,因此不保证$rk_l<rk_r$,需要$swap$。

    其实这道题虽然耗了很久,但因为没有这样的思路,只是单纯的颓着题解和代码,无疑是浪费的时间的。

    但如果真的会了这道题,那就另当别论了。

    E. SvT

    分治,lcp。

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

  • 相关阅读:
    转载(腾讯云社区)——详解django-apscheduler的使用方法
    pipenv——python包管理工具
    xx系统需求进度02
    xx系统需求进度01
    Hbase简介
    第七周总结
    《软件需求十步走》阅读笔记一
    第六周总结
    HDFS
    金字塔表达方法
  • 原文地址:https://www.cnblogs.com/hzoi2018-xuefeng/p/12093115.html
Copyright © 2011-2022 走看看