zoukankan      html  css  js  c++  java
  • $SA$后缀数组

    迪哥的博客写得钛皓蜡,板子注释也很清晰。

    所以这里只有题表辣。


     差异:

      首先拆柿子:

      $$frac{sum_{i=1}^{n}{(n-2)*i+frac{n*(n+1)}{2}}}{2}-sum_{1leq i< jleq n}{2*lcp(i,j)}$$
      $$frac{n*(n-1)*(n+1)}{2}-sum_{1leq i< jleq n}{2*lcp(i,j)}$$

      后面的$lcp$可以用单调栈求出每个$height(i)$控制的区间,$O(n)$解决。

      注意一个$height$对应区间长度的至少是$2$,并且$height(1)$无用。


     Sandy的卡片:

      把字符串加上分割符建$sa$,然后二分长度,$O(n*logn)$。


     相似子串:

      手玩发现子串的排名和子串所在的后缀有关。

      把每个后缀串控制的排名范围求出,即可$lower\_bound$出排名对应的子串。

      正反建$sa$,$ST$表维护公共前缀即可。


     品酒大会:

      第一问和差异一样,第二问直接$mid$的两边$ST$表。

      因为“$r$相似同时也是$r-1$相似的”,所以要后缀取$max$,然而窝脑抽打的线段树区间修改

      注意数据结构的操作是套在$rk$上的,下标取值一定要写对。


     字符串:

      读错题了。。。

      询问是“子串$s[a..b]$的所有子串”和“$s[c..d]$”的最长公共前缀。

      两种解法:

        1>大神做法:

          二分答案,对$rk$维护主席树。当有$mid$时,后缀可取区间为$[a,b-mid+1]$。

          那么在区间$[a,b-mid+1]$的主席树上查的前驱后继一定是在排名上最接近以$c$开头的后缀。

          也是$lcp$最大的位置,判断即可。

        2>常数巨大的做法:

          也二分长度,根据$mid$再二分出排名上符合$lcp>=mid$的区间,

          然后用主席树判断$[a,b-mid+1]$是否有在合法范围的排名。

          理论上也是一个$log$。


     喵星球上的点名:

      数据比较水,暴扫可过。

      看网上有十几种做法,字符串家族八仙过海。

      这里采纳了一个很实用很清真的树状数组做法。

      第一问:

        数颜色,可用主席树。

        树状数组的做法是先离线,求每个颜色的$pre$。

        然后边扫边统计答案,到某个颜色时把$pre$上的贡献删掉,前缀做差得答案。

        原理是只留下了一个区间同种颜色的最后一个。

      第二问:

        也是边扫边用树状数组。

        扫到位置$i$,把询问区间左端点在$i$上的加入。

        $i$与$pre[i]$前缀和相减得到左端点在这之间的个数。

        接下来把区间右端点在$i$的左端点的位置贡献减去,保证了不会统计到夹在$i$到$pre[i]$的区间。

      神仙树状数组做法。

      1 #include<cstdio>
      2 #include<iostream>
      3 #include<cstring>
      4 #include<vector>
      5 #define MAXN 490001
      6 #define lowbit(x) (x&-x)
      7 using namespace std;
      8 inline int read(){
      9     int s=0,w=0;char ch=getchar();
     10     while(ch<'0'||ch>'9')w|=(ch=='-'),ch=getchar();
     11     while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
     12     return w?-s:s;
     13 }
     14 #define kd (read())
     15 int x[MAXN],y[MAXN],c[MAXN];
     16 int a[MAXN],sa[MAXN],rk[MAXN],h[MAXN];
     17 int n,m;
     18 int f[19][MAXN];
     19 int LOG[MAXN];
     20 inline int Getmn(int l,int r){
     21     if(r<l)return 0x7fffffff;
     22     int len=LOG[r-l+1];
     23     return min(f[len][l],f[len][r-(1<<len)+1]);
     24 }
     25 void init(){
     26     m=10002;
     27     for(int i=1;i<=m;++i)c[i]=0;
     28     for(int i=1;i<=n;++i)++c[x[i]=a[i]];
     29     for(int i=1;i<=m;++i)c[i]+=c[i-1];
     30     for(int i=1;i<=n;++i)sa[c[x[i]]--]=i;
     31     for(int len=1;len<=n;len<<=1){
     32         int num=0;
     33         for(int i=n-len+1;i<=n;++i)y[++num]=i;
     34         for(int i=1;i<=n;++i)if(sa[i]>len)y[++num]=sa[i]-len;
     35         for(int i=1;i<=m;++i)c[i]=0;
     36         for(int i=1;i<=n;++i)++c[x[i]];
     37         for(int i=1;i<=m;++i)c[i]+=c[i-1];
     38         for(int i=n;i>=1;--i)sa[c[x[y[i]]]--]=y[i];
     39         for(int i=1;i<=n;++i)y[i]=x[i],x[i]=0;
     40         x[sa[1]]=m=1;
     41         for(int i=2;i<=n;++i)x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&y[sa[i]+len]==y[sa[i-1]+len])?m:++m;
     42         if(m==n)break;
     43     }
     44     for(int i=1;i<=n;++i)rk[sa[i]]=i;
     45     for(int i=1,k=0;i<=n;h[rk[i++]]=k,k=k?k-1:0)
     46         while(a[i+k]==a[sa[rk[i]-1]+k])++k;
     47     for(int i=1;i<=n;++i)f[0][i]=h[i];
     48     for(int j=1;j<=18;++j)
     49         for(int i=1;i+(1<<j)-1<=n;++i)
     50             f[j][i]=min(f[j-1][i],f[j-1][i+(1<<j-1)]);
     51     return ;
     52 }
     53 int N,M;
     54 int nw[MAXN],pre[MAXN];
     55 int belong[MAXN],id[MAXN],Len[MAXN];
     56 int an1[MAXN],an2[MAXN];
     57 int C[MAXN];
     58 inline void addC(int pos,int val){
     59     if(!pos)return ;
     60     for(;pos<=n;pos+=lowbit(pos))C[pos]+=val;
     61     return ;
     62 }
     63 inline int Getsm(int pos){
     64     int res=0;
     65     for(;pos;pos-=lowbit(pos))res+=C[pos];
     66     return res;
     67 }
     68 struct node{
     69     int l,r,idx;
     70 };
     71 vector<node >td[MAXN];
     72 vector<int >dd[MAXN];
     73 int lei[MAXN];
     74 int main(){
     75     //freopen("da.in","r",stdin);
     76     //freopen("2.out","w",stdout);
     77 
     78     N=kd,M=kd;
     79     for(int i=1,llen,tmp;i<=N;++i){
     80         llen=kd;
     81         while(llen--)tmp=kd+1,a[++n]=tmp,belong[n]=i;
     82         a[++n]=10002;
     83         llen=kd;
     84         while(llen--)tmp=kd+1,a[++n]=tmp,belong[n]=i;
     85         a[++n]=10002;
     86     }
     87     for(int i=1,llen,tmp;i<=M;++i){
     88         id[i]=n+1;Len[i]=llen=kd;
     89         while(llen--)tmp=kd+1,a[++n]=tmp;
     90         a[++n]=10002;
     91     }
     92     for(int i=2;i<=n;++i)LOG[i]=LOG[i>>1]+1;
     93     init();
     94     for(int i=1;i<=M;++i){
     95         int l=1,r=rk[id[i]],posl=0,posr=0;
     96         while(r-l>1){
     97             int mid=l+r>>1;
     98             if(Getmn(mid+1,rk[id[i]])>=Len[i])r=mid;
     99             else l=mid;
    100         }
    101         if(Getmn(l+1,rk[id[i]])>=Len[i])posl=l;
    102         else posl=r;
    103         l=rk[id[i]],r=n;
    104         while(r-l>1){
    105             int mid=l+r>>1;
    106             if(Getmn(rk[id[i]]+1,mid)>=Len[i])l=mid;
    107             else r=mid;
    108         }
    109         if(Getmn(rk[id[i]]+1,r)>=Len[i])posr=r;
    110         else posr=l;
    111         td[posr].push_back((node){posl,posr,i});
    112         dd[posr].push_back(posl);
    113         ++lei[posl];
    114         //cout<<posl<<" "<<posr<<endl;
    115     }
    116     for(int i=1;i<=n;++i)
    117         if(belong[sa[i]]){
    118             pre[i]=nw[belong[sa[i]]];
    119             nw[belong[sa[i]]]=i;
    120         }
    121     for(int i=1;i<=n;++i){
    122         if(belong[sa[i]]){
    123             addC(pre[i],-1);
    124             addC(i,1);
    125         }
    126         for(int k=0;k<td[i].size();++k){
    127             node tmp=td[i][k];
    128             an1[tmp.idx]=Getsm(tmp.r)-Getsm(tmp.l-1);
    129         }
    130     }
    131     for(int i=1;i<=M;++i)printf("%d
    ",an1[i]);
    132     for(int i=0;i<=n;++i)C[i]=0;
    133     for(int i=1;i<=n;++i){
    134         addC(i,lei[i]);
    135         if(belong[sa[i]])
    136             an2[belong[sa[i]]]+=Getsm(i)-Getsm(pre[i]);
    137         for(int k=0;k<dd[i].size();++k)addC(dd[i][k],-1);
    138     }
    139     for(int i=1;i<=N;++i)printf("%d ",an2[i]);
    140     puts("");
    141     
    142     return 0;
    143 }
    View Code

     外星联络:

      发现$N$很小,枚举子串,单调指针随便扫。


    SvT:

      完序,$unique$就变成"队长快跑"辣,然后线段树常数很大是卡过的。

      仍然可以用$h$控制区间跨区间乘法原理的思想。

      大神都不写线段树。


    跳蚤:

      上来思路偏了,莫名想起模拟测试里的类似单调性优化$dp$。

      然后想怎么快速求出一个区间的贡献,没法求,kuku。

      然后颓了一眼标签,是二分。

      思路来了,因为跑完$sa$,可以得到子串的排名。

      考虑二分这个排名是否可行。

      那就要看是否能$check$,以及合法的时间复杂度。

      发现反向贪心是可以的。

      至于正向为何不太行,是因为在考虑一个点能否加入,就要看他对字典序的最大贡献,最大贡献一定是右端点在自己所在的块的右边界上。而只有反向才能确定这个右边界。

      然后是解决如何判断一个点能否加入,即是看此点$x$和右边界$now$的子串的排名和二分的排名对比。

      一开始我想再来一个二分此子串的排名,然而没有$Mouding$脸白。

      又看了发题解,发现$lcp$有大用处,可以先得到二分排名的串,和要判断的串求$lcp+1$位字典序比较即可。

      所以我好菜啊。


    股市的预测:

      因为是趋势,所以先差分。枚举相同部分的长度,以此长度分块,有一个$L$,$R=L+M+len$,然后找$L,R$的最长公共前缀后缀,即答案可以在这个区间里左右挪动。然后细节很多。


     总结:

      单调栈,二分很常见。

      建完$sa$就变成数据结构题辣!

      $hash$暴力能刚好多题。。。

      

     

     

     

     

     

     

     

     

      

     

  • 相关阅读:
    flask多线程多协程操作
    flask介绍
    centos django+Nginx+uwsgi部署
    centos下运行python3.6+Django+mysql项目
    centos虚拟机下安装nginx
    redis安装
    路飞学城课程_课程详细_作业点评
    redis使用方式
    git命令学习
    组合&多态&封装
  • 原文地址:https://www.cnblogs.com/2018hzoicyf/p/12085194.html
Copyright © 2011-2022 走看看