zoukankan      html  css  js  c++  java
  • BZOJ4556:[TJOIHEOI2016]字符串(后缀数组,主席树,二分,ST表)

    Description

    佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物。生日礼物放在一个神奇的箱子中。箱子外边写了一个长为n的字符串s,和m个问题。佳媛姐姐必须正确回答这m个问题,才能打开箱子拿到礼物,升职加薪,出任CEO,嫁给高富帅,走上人生巅峰。每个问题均有a,b,c,d四个参数,问你子串s[a..b]的所有子串和s[c..d]的最长公共前缀的长度的最大值是多少?佳媛姐姐并不擅长做这样的问题,所以她向你求助,你该如何帮助她呢?

    Input

    输入的第一行有两个正整数n,m,分别表示字符串的长度和询问的个数。接下来一行是一个长为n的字符串。接下来m行,每行有4个数a,b,c,d,表示询问s[a..b]的所有子串和s[c..d]的最长公共前缀的最大值。1<=n,m<=100,000,
    字符串中仅有小写英文字母,a<=b,c<=d,1<=a,b,c,d<=n

    Output

     对于每一次询问,输出答案。

    Sample Input

    5 5
    aaaaa
    1 1 1 5
    1 5 1 1
    2 3 2 3
    2 4 2 3
    2 3 2 4

    Sample Output

    1
    1
    2
    2
    2

    Solution

    设$suf_i$表示后缀$i$,

    首先考虑二分一个答案$mid$,那么现在问题转化成求$suf_a$到$suf_{b-mid+1}$的这些个后缀中,是否有一个后缀和$suf(c)$的$lcp$大于等于$mid$。

    设$SA_i$表示后缀排序排名为$i$的后缀,$Rank_i$表示$suf_i$的后缀排序排名。

    由于对于$suf_c$,我们可以用二分+$ST$表求出后缀排序中的可行区间,可行区间内的后缀和$suc_c$的$lcp$长度都大于等于$mid$。现在问题转化为了求$suf_a$到$suf_{b-mid+1}$的这些后缀是否哪个后缀的$Rank$值在可行区间内,这个用一个主席树就好了。

    Code

      1 #include<iostream>
      2 #include<cstring>
      3 #include<cstdio>
      4 #define N (100009)
      5 using namespace std;
      6 
      7 struct Sgt{int ls,rs,val;}Segt[N*18];
      8 int Root[N],sgt_num;
      9 int n,q,m=26,a,b,c,d;
     10 int ST[N][18],LOG2[N];
     11 int wa[N],wb[N],wt[N],SA[N],Height[N],Rank[N];
     12 char r[N];
     13 
     14 inline int read()
     15 {
     16     int x=0,w=1; char c=getchar();
     17     while (c<'0' || c>'9') {if (c=='-') w=-1; c=getchar();}
     18     while (c>='0' && c<='9') x=x*10+c-'0', c=getchar();
     19     return x*w;
     20 }
     21 
     22 bool cmp(int *y,int a,int b,int k)
     23 {
     24     int arank1=y[a];
     25     int brank1=y[b];
     26     int arank2=a+k>=n?-1:y[a+k];
     27     int brank2=b+k>=n?-1:y[b+k];
     28     return arank1==brank1 && arank2==brank2;
     29 }
     30 
     31 void Build_SA()
     32 {
     33     int *x=wa,*y=wb;
     34     for (int i=0; i<m; ++i) wt[i]=0;
     35     for (int i=0; i<n; ++i) ++wt[x[i]=r[i]-'a'];
     36     for (int i=1; i<m; ++i) wt[i]+=wt[i-1];
     37     for (int i=n-1; i>=0; --i) SA[--wt[x[i]]]=i;
     38     
     39     for (int j=1; j<=n; j<<=1)
     40     {
     41         int p=0;
     42         for (int i=n-j; i<n; ++i) y[p++]=i;
     43         for (int i=0; i<n; ++i) if (SA[i]>=j) y[p++]=SA[i]-j;
     44         
     45         for (int i=0; i<m; ++i) wt[i]=0;
     46         for (int i=0; i<n; ++i) ++wt[x[y[i]]];
     47         for (int i=1; i<m; ++i) wt[i]+=wt[i-1];
     48         for (int i=n-1; i>=0; --i) SA[--wt[x[y[i]]]]=y[i];
     49 
     50         m=1; swap(x,y);
     51         x[SA[0]]=0;
     52         for (int i=1; i<n; ++i)
     53             x[SA[i]]=cmp(y,SA[i],SA[i-1],j)?m-1:m++;
     54         if (m>=n) break;
     55     }
     56 }
     57 
     58 void Build_Height()
     59 {
     60     for (int i=0; i<n; ++i) Rank[SA[i]]=i;
     61     int k=0;
     62     for (int i=0; i<n; ++i)
     63     {
     64         if (!Rank[i]) continue;
     65         if (k) k--;
     66         int j=SA[Rank[i]-1];
     67         while (r[i+k]==r[j+k]) ++k;
     68         Height[Rank[i]]=k;
     69     }
     70 }
     71 
     72 void Build_ST()
     73 {
     74     for (int i=2; i<=n; ++i) LOG2[i]=LOG2[i>>1]+1;
     75     for (int i=0; i<n; ++i) ST[i][0]=Height[i];
     76     for (int j=1; j<=17; ++j)
     77         for (int i=0; i+(1<<j)-1<n; ++i)
     78             ST[i][j]=min(ST[i][j-1],ST[i+(1<<j-1)][j-1]);
     79 }
     80 
     81 int Query(int l,int r)
     82 {
     83     int k=LOG2[r-l+1];
     84     return min(ST[l][k],ST[r-(1<<k)+1][k]);
     85 }
     86 
     87 int Update(int pre,int l,int r,int x)
     88 {
     89     int now=++sgt_num;
     90     Segt[now]=Segt[pre];
     91     Segt[now].val++;
     92     if (l==r) return now;
     93     int mid=(l+r)>>1;
     94     if (x<=mid) Segt[now].ls=Update(Segt[now].ls,l,mid,x);
     95     else Segt[now].rs=Update(Segt[now].rs,mid+1,r,x);
     96     return now;
     97 }
     98 
     99 int Query(int u,int v,int l,int r,int l1,int r1)
    100 {
    101     if (l>r1 || r<l1) return 0;
    102     if (l1<=l && r<=r1) return Segt[v].val-Segt[u].val;
    103     int mid=(l+r)>>1;
    104     return Query(Segt[u].ls,Segt[v].ls,l,mid,l1,r1)+Query(Segt[u].rs,Segt[v].rs,mid+1,r,l1,r1);
    105 }
    106 
    107 bool check(int lim)
    108 {
    109     int l,r,L,R;
    110     l=0,r=Rank[c]-1,L=Rank[c];
    111     while  (l<=r)
    112     {
    113         int mid=(l+r)>>1;
    114         if (Query(mid+1,Rank[c])>=lim) L=mid,r=mid-1;
    115         else l=mid+1;
    116     }
    117     l=Rank[c]+1,r=n-1,R=Rank[c];
    118     while (l<=r)
    119     {
    120         int mid=(l+r)>>1;
    121         if (Query(Rank[c]+1,mid)>=lim) R=mid,l=mid+1;
    122         else r=mid-1;
    123     }
    124     if (Query(Root[a],Root[b-lim+2],0,n,L,R)) return true;
    125     return false;
    126 }
    127 
    128 int main()
    129 {
    130     n=read(); q=read();
    131     scanf("%s",r);
    132     Build_SA(); Build_Height();
    133     Build_ST();
    134     for (int i=1; i<=n; ++i)
    135         Root[i]=Update(Root[i-1],0,n,Rank[i-1]);
    136     while (q--)
    137     {
    138         a=read()-1; b=read()-1; c=read()-1; d=read()-1;
    139         int l=1,r=min(b-a+1,d-c+1),ans=0;
    140         while (l<=r)
    141         {
    142             int mid=(l+r)>>1;
    143             if (check(mid)) ans=mid, l=mid+1;
    144             else r=mid-1;
    145         }
    146         printf("%d
    ",ans);
    147     }
    148 }
  • 相关阅读:
    PHP 操作MySQL时mysql_connect( )和Mysqli( )的两种报错机制
    OS + macOS Mojave 10.14.4 / sushi / ssh-keygen / ssh-copy-id
    script ajax / XHR / XMLHttpRequest
    java socket / No buffer space available
    OS + Ubuntu ARM Android
    mysql中批量替换数据库中的内容的sql
    linux下ubuntu系统安装及开发环境配置
    PHP 截取字符串专题
    在Ubuntu中用root帐号登录
    理解javascript的caller,callee,call,apply概念
  • 原文地址:https://www.cnblogs.com/refun/p/10407235.html
Copyright © 2011-2022 走看看