zoukankan      html  css  js  c++  java
  • 「专题总结」后缀数组

    SA能做的事:
    1.求lcp、lcs
    2.求本质不同的子串数
    3.比较子串字典序
    4.给定子串求排名
    5.给定排名求子串
    6.求子串出现次数
    7.求多串最长公共子串
    以上“子串”意为SA处理的串的子串,可以是输入给出,然后接在后面。


    模板
    
    void SA(char *s,int m=26){
    	F(i,1,m)c[i]=0;
    	F(i,1,n)++c[x[i]=s[i]-'a'+1];
    	F(i,1,m)c[i]+=c[i-1];
    	F(i,1,n)sa[c[x[i]]--]=i;
    	for(reg int len=1,num;len<=n;len<<=1){num=0;
    		F(i,n-len+1,n)y[++num]=i;
    		F(i,1,n)if(sa[i]>len)y[++num]=sa[i]-len;
    		F(i,1,m)c[i]=0;
    		F(i,1,n)++c[x[i]];
    		F(i,1,m)c[i]+=c[i-1];
    		for(reg int i=n;i;--i)sa[c[x[y[i]]]--]=y[i];
    		F(i,1,n)y[i]=x[i],x[i]=0;
    		x[sa[1]]=m=1;
    		F(i,2,n)x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+len]==y[sa[i-1]+len])?m:++m;
    		if(m==n)break;
    	}
    	F(i,1,n)rk[sa[i]]=i;
    	for(reg int i=1,k=0;i<=n;h[rk[i++]]=k,k-=k>0)
    		while(i+k<=n&&sa[rk[i]-1]+k<=n&&s[i+k]==s[sa[rk[i]-1]+k])++k;
    }
    

    A. Sandy的卡片

    把原串差分,问题转化为求N个串的最长公共子串

    trick:多串同时比较,把所有串卡在一起,并用空字符隔开。

    公共子串的相似度高,在字典上一定连续,给每个后缀打上原串标记。
    问题转化为求包含n个不同标记的区间的lcp的最大值
    显然区间缩小答案不会变差,所以用双指针在字典上扫一遍即可。

    B. 喵星球上的点名

    两个字符串代表一只喵,所以不好统计答案。
    把所有串接在一起处理SA。
    一只喵看成一种颜色,把其的两个串都染上颜色。
    对点名串二分出它作为子串的区间。
    那么第一问实质在求一个区间内有多少不同颜色,可以排序,记录pre,然后用BIT优化。同《HH的项链》
    第二问求每种颜色被多少区间包含,我不会(nlogn)。莫队比较好做,记录每种颜色进入区间的时间,然后在该颜色全部退出区间时统计存在时间即可。(然后第一问也顺便做了)

    C. 字符串

    在字典上离得近lcp肯定越优
    难点在于有区间限制,考虑主席树维护原串区间rk[i],然后树上查询rk[c]的前趋后继取最优。
    以上做法存在一定问题:有可能得出的答案超出了b的限制。考虑二分答案,在二分限制的主席树范围上进行查询。

    D. 差异

    难点在于求(sumlimits_{1 leq i < j leq n}lcp(T_i,T_j))
    所有点对的lcp,那就可以直接在h数组上统计。
    考虑(h_i)作为最小值的范围,一定是跨过(i)的一段区间。
    单调栈找到左边和右边第一个比(h_i)小的数,注意处理相同元素,方法是一边>=,一边>
    也可以直接递归建树,用RMQ快速找到最值。

    E. 相似子串

    • 给定子串rank查位置、长度
      把h数组想象成一本字典,从上到下按字典序排序。
      预处理sum数组,(sum[n]=sumlimits_{i=1}^{n}length-sa[i]+1-h[i])
      在sum上二分到子串所在区间(x),长度就是(h[x]+rank-sum[x-1])

    • 求给定位置两子串的lcp、lcs。
      正反求SA,RMQ
      或者像我一样懒,lcs暴力hash

    F. 品酒大会

    我做法很蠢,启发式合并,还有一层无用循环(nlog^2n)和bit
    「r相似」即lcp为r
    第一问:用单调栈统计,差分,最后做前缀和得答案
    第二问:RMQ预处理区间最大值和最小值,一定是最值相乘。

    G.外星联络

    求所有本质不同子串的出现顺序,按字典序输出。
    处理SA,利用h[]保留信息,优化到N^2

    H.跳蚤

    一开始的想法是区间划分dp,状态数是NK的,转移可以用三分优化,同时需要主席树。(NKlog^2N)不可做
    换个思路,最大值最小,考虑二分子串的排名。不难发现具有单调性。
    倒序check,若首字母大则return 0,否则在后边割开,划分次数大于k return 0。
    没有想到二分子串排名

    I.股市的预测

    大神题。求有多少形如ABA的串,B的长度给定
    暴力需要枚举A的位置和长度。
    枚举A的长度len,把N以len为间隔划分开,每个区间设置“关键点”,发现两端A一定会各自覆盖一个且仅一个“关键点”。
    以len为增量枚举位置i,第一个A一定只覆盖i,那么另一端j=i+m+len一定只被第二个A覆盖。
    尝试从i,j向两端扩展出区间,即求lcp和lcs,同时都要和len取min,不然一个A覆盖两个关键点,会算重。
    若lcp+lcs-(lcp&&lcs)>=len,贡献为 lcp+lcs-(lcp&&lcs)-len+1,len可以在上面滑动。
    调和级数。复杂度(nlnn)

    J.SvT

    (sum t leq 3 imes10^6)所以同样用单调栈求点对lcp,不同在于是在RMQ上做单调栈

  • 相关阅读:
    linux 监控(未完...)
    linux命令快速入门
    [转]开源语音数据集
    websocket-client 试用简记
    gevent BrokenPipeError[未解决]
    locust 试用简记
    pydub 把音频按时间段切成小段(结合ffmpeg)
    ffmpeg & ffmpeg-python 试用(音频)
    Vue父组件向子组件传递数据
    uni-app 中 使用 scroll-view 想要手指触摸滑动,竟然不生效
  • 原文地址:https://www.cnblogs.com/hzoi-yzh/p/12095840.html
Copyright © 2011-2022 走看看