zoukankan      html  css  js  c++  java
  • Bzoj4556 [Tjoi2016&Heoi2016]字符串

    Time Limit: 20 Sec  Memory Limit: 128 MB
    Submit: 846  Solved: 327

    Description

    佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物。生日礼物放在一个神奇的箱子中。箱子外边写了
    一个长为n的字符串s,和m个问题。佳媛姐姐必须正确回答这m个问题,才能打开箱子拿到礼物,升职加薪,出任CE
    O,嫁给高富帅,走上人生巅峰。每个问题均有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

    HINT

     

    Source

    解法1:后缀自动机+二分答案+子串定位+倍增+线段树合并

    后缀自动机可以求最长公共后缀,为了解决这个问题我们需要把输入的子串和询问全都翻转,把问题变成求解最长公共后缀。

    在建自动机时记录字符串每个位置到达的自动机上结点,同时用线段树记录每个结点能到达的所有状态。

    二分答案,假设我们要求的LCP长度为lim,那么我们需要从c~d字串对应的自动机结点倍增上溯,找到最浅的mx[]值>=lim的fa结点,查询它的线段树中有没有下标在a+mid-1 ~ b  (询问也翻转过了)中的点。

      1 #include<iostream>
      2 #include<algorithm>
      3 #include<cstdio>
      4 #include<cstring>
      5 using namespace std;
      6 const int mxn=200010;
      7 int read(){
      8     int x=0,f=1;char ch=getchar();
      9     while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
     10     while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
     11     return x*f;
     12 }
     13 struct node{
     14     int l,r;
     15 }t[mxn*50];
     16 int rot[mxn],cnt=0;
     17 void update(int p,int l,int r,int &rt){
     18     rt=++cnt;
     19     if(l==r)return;
     20     int mid=(l+r)>>1;
     21     if(p<=mid)update(p,l,mid,t[rt].l);
     22     else update(p,mid+1,r,t[rt].r);
     23     return;
     24 }
     25 int merge(int x,int y){
     26     if(x*y==0)return x+y;
     27     int now=++cnt;
     28     t[now].l=merge(t[x].l,t[y].l);
     29     t[now].r=merge(t[x].r,t[y].r);
     30     return now;
     31 }
     32 bool query(int L,int R,int l,int r,int rt){
     33     if(!rt)return 0;
     34     if(L<=l && r<=R)return 1;
     35     int mid=(l+r)>>1;
     36     if(L<=mid) if(query(L,R,l,mid,t[rt].l))return 1;
     37     if(R>mid) if(query(L,R,mid+1,r,t[rt].r))return 1;
     38     return 0;
     39 }
     40 int n,m;
     41 int pos[mxn];
     42 struct SAM{
     43     int t[mxn][26],fa[mxn][19];
     44     int mx[mxn],w[mxn],rk[mxn];
     45     int S,cnt,last;
     46     void init(){
     47         S=cnt=last=1;
     48         return;
     49     }
     50     void insert(int c,int id){
     51         int np=++cnt,p=last;last=np;
     52         pos[id]=np;
     53         mx[np]=mx[p]+1;
     54         update(id,1,n,rot[np]);
     55         for(;p && !t[p][c];p=fa[p][0])t[p][c]=np;
     56         if(!p){
     57             fa[np][0]=S;
     58         }
     59         else{
     60             int q=t[p][c];
     61             if(mx[q]==mx[p]+1){
     62                 fa[np][0]=q;
     63             }
     64             else{
     65                 int nq=++cnt;
     66                 mx[nq]=mx[p]+1;
     67                 memcpy(t[nq],t[q],sizeof t[q]);
     68                 fa[nq][0]=fa[q][0];
     69                 fa[q][0]=fa[np][0]=nq;
     70                 for(;p && t[p][c]==q;p=fa[p][0])t[p][c]=nq;
     71             }
     72         }
     73         return;
     74     }
     75     void st(){
     76         for(int i=1;i<=cnt;i++)w[mx[i]]++;
     77         for(int i=1;i<=cnt;i++)w[i]+=w[i-1];
     78         for(int i=1;i<=cnt;i++)rk[w[mx[i]]--]=i;
     79         for(int i=cnt;i;i--){
     80             int t=rk[i],f=fa[t][0];
     81             rot[f]=merge(rot[f],rot[t]);
     82         }
     83         for(int j=1;j<=18;j++)
     84             for(int i=1;i<=cnt;i++)
     85                 fa[i][j]=fa[fa[i][j-1]][j-1];
     86         return;
     87     }
     88     int find(int L,int R,int x,int lim){
     89 //        printf("mid:%d x:%d l:%d r:%d
    ",lim,x,L,R);
     90         for(int i=18;i>=0;i--)if(mx[fa[x][i]]>=lim)x=fa[x][i];
     91         return query(L,R,1,n,rot[x]);
     92     }
     93 }sa;
     94 void solve(){
     95     int a,b,c,d;
     96     while(m--){
     97         a=read();b=read();c=read();d=read();
     98         a=n-a+1;b=n-b+1;c=n-c+1;d=n-d+1;
     99         swap(a,b);swap(c,d);
    100         int l=1,r=min(d-c+1,b-a+1),mid;
    101         int ans=0;
    102         while(l<=r){
    103             mid=(l+r)>>1;
    104             if(sa.find(a+mid-1,b,pos[d],mid)){
    105                 ans=mid;
    106                 l=mid+1;
    107             }
    108             else r=mid-1;
    109         }
    110         printf("%d
    ",ans);
    111     }
    112     return;
    113 }
    114 char s[mxn];
    115 int main(){
    116     int i,j;
    117     scanf("%d%d",&n,&m);
    118     scanf("%s",s+1);
    119     reverse(s+1,s+n+1);
    120     sa.init();
    121     for(i=1;i<=n;i++)
    122         sa.insert(s[i]-'a',i);
    123     sa.st();
    124     solve();
    125     return 0;
    126 }

    解法2:后缀数组+二分答案+ST表+主席树

    同样是二分答案的思想,二分答案为lim,利用ST表和后缀数组的HT数组查询a~b-lim+1  (询问和字串都没有翻转)范围内有没有ht[]>=lim的点

      1 /*by SilverN*/
      2 #include<algorithm>
      3 #include<iostream>
      4 #include<cstring>
      5 #include<cstdio>
      6 #include<cmath>
      7 #include<vector>
      8 using namespace std;
      9 const int mxn=100010;
     10 int read(){
     11     int x=0,f=1;char ch=getchar();
     12     while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
     13     while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
     14     return x*f;
     15 }
     16 int sa[mxn],rk[mxn],ht[mxn];
     17 int wa[mxn],wb[mxn],wv[mxn],cnt[mxn];
     18 char s[mxn];
     19 inline int scmp(int *r,int a,int b,int l){
     20     return r[a]==r[b] && r[a+l]==r[b+l];
     21 }
     22 void GetSA(int *sa,int n,int m){
     23     int i,j,k;
     24     int *x=wa,*y=wb;
     25     for(i=0;i<m;i++)cnt[i]=0;
     26     for(i=0;i<n;i++)cnt[x[i]=s[i]-'a'+1]++;
     27     for(i=1;i<m;i++)cnt[i]+=cnt[i-1];
     28     for(i=n-1;i>=0;i--)sa[--cnt[x[i]]]=i;
     29 //    printf("fin
    ");
     30     for(int j=1,p=0;p<n;j<<=1,m=p){
     31         for(p=0,i=n-j;i<n;i++)y[p++]=i;
     32         for(i=0;i<n;i++)
     33             if(sa[i]>=j)y[p++]=sa[i]-j;
     34         for(i=0;i<n;i++)wv[i]=x[y[i]];
     35         for(i=0;i<m;i++)cnt[i]=0;
     36         for(i=0;i<n;i++)cnt[wv[i]]++;
     37         for(i=1;i<m;i++)cnt[i]+=cnt[i-1];
     38         for(i=n-1;i>=0;i--)sa[--cnt[wv[i]]]=y[i];
     39         swap(x,y);
     40         p=1;x[sa[0]]=0;
     41         for(i=1;i<n;i++)
     42             x[sa[i]]=scmp(y,sa[i],sa[i-1],j)?p-1:p++;
     43     }
     44 //    printf("fin
    ");
     45     return;
     46 }
     47 void GetHT(int n){
     48     int i,j,k=0;
     49 //    for(i=0;i<n;i++)sa[i]=sa[i+1];
     50     for(i=1;i<=n;i++)rk[sa[i]]=i;
     51     for(i=0;i<n;i++){
     52         if(k)k--;
     53         if(!rk[i])continue;
     54         j=sa[rk[i]-1];
     55         while(s[i+k]==s[j+k])k++;
     56         ht[rk[i]]=k;
     57     }
     58     return;
     59 }
     60 int n,m;
     61 void Debug(){
     62     int i;
     63     for(i=1;i<=n;i++)printf("%d ",sa[i]);puts("");
     64     for(i=0;i<n;i++)printf("%d ",rk[i]);puts("");
     65     for(i=1;i<=n;i++)printf("%d ",ht[i]);puts("");
     66     return;
     67 }
     68 //
     69 struct node{
     70     int l,r;
     71     int sz;
     72 }t[mxn*60];
     73 int rot[mxn],tct=0;
     74 void update(int p,int v,int l,int r,int y,int &rt){
     75     rt=++tct;
     76     t[rt]=t[y];t[rt].sz+=v;
     77     if(l==r)return;
     78     int mid=(l+r)>>1;
     79     if(p<=mid)update(p,v,l,mid,t[y].l,t[rt].l);
     80     else update(p,v,mid+1,r,t[y].r,t[rt].r);
     81     return;
     82 }
     83 int have;
     84 void query(int L,int R,int l,int r,int y,int rt){
     85     if(!(t[rt].sz-t[y].sz) || have)return;
     86     if(L<=l && r<=R){
     87         have+=t[rt].sz-t[y].sz;return;
     88     }
     89     int mid=(l+r)>>1;
     90     if(L<=mid)query(L,R,l,mid,t[y].l,t[rt].l);
     91     if(R>mid)query(L,R,mid+1,r,t[y].r,t[rt].r);
     92     return;
     93 }
     94 //
     95 namespace STT{
     96     int lg[mxn];
     97     int st[mxn][19];
     98     void ST_init(){
     99         for(int i=1,h=0;i<=n;i++){
    100             while((1<<(h+1))<=i)h++;
    101             lg[i]=h;
    102         }
    103         for(int i=1;i<=n;i++)st[i][0]=ht[i];
    104         for(int j=1;j<=17;j++)
    105             for(int i=1;i<=n;i++)
    106                 if(i+(1<<j)-1<=n)
    107                     st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]);
    108                 else break;
    109         return;
    110     }
    111     inline int min(int a,int b){return a<b?a:b;}
    112     inline int STQ(int l,int r){
    113 //        l++;r++;
    114         int k=lg[r-l+1];
    115 /*        printf("l:%d r:%d k:%d L:%d R:%d
    ",
    116             l,r,k,st[l][k],st[r-(1<<k)+1][k]);*/
    117         return min(st[l][k],st[r-(1<<k)+1][k]);
    118     }
    119 }
    120 //
    121 int findL(int lim,int c){
    122     using namespace STT;
    123     int res=-1;
    124     int l=2,r=rk[c-1];
    125     while(l<=r){
    126         int mid=(l+r)>>1;
    127 /*        printf("mid:%d M:%d ST:%d
    ",mid,M,STQ(mid,M));*/
    128         if(STQ(mid,rk[c-1])>=lim){
    129             res=mid;
    130             r=mid-1;
    131         }
    132         else l=mid+1;
    133     }
    134     return res;
    135 }
    136 int findR(int lim,int c){
    137     using namespace STT;
    138     int res=-1;
    139     int l=rk[c-1]+1,r=n;
    140     while(l<=r){
    141         int mid=(l+r)>>1;
    142         if(STQ(rk[c-1]+1,mid)>=lim){
    143             res=mid;
    144             l=mid+1;
    145         }
    146         else r=mid-1;
    147     }
    148     return res;
    149 }
    150 //
    151 int a,b,c,d;
    152 bool check(int lim){
    153     have=0;
    154     int L=findL(lim,c);
    155     int R=findR(lim,c);
    156 //    printf("lim:%d L:%d c:%d R:%d
    ",lim,L,rk[c-1],R);
    157     if(L==-1)L=rk[c-1];
    158     else L=L-2;
    159     query(a,b-lim+1,1,n,rot[L],rot[rk[c-1]]);
    160     if(have)return 1;
    161     if(R==-1)R=rk[c-1];
    162     else R=R;
    163     query(a,b-lim+1,1,n,rot[rk[c-1]],rot[R]);
    164     return have;
    165 }
    166 void solve(){
    167     int i,j;
    168     a=read();b=read();c=read();d=read();
    169     int l=1,r=min(d-c+1,b-a+1),res=0;
    170     while(l<=r){
    171 //        printf("l:%d r:%d
    ",l,r);
    172         int mid=(l+r)>>1;
    173         if(check(mid)){
    174             res=mid;
    175             l=mid+1;
    176         }
    177         else r=mid-1;
    178     }
    179     printf("%d
    ",res);
    180     return;
    181 }
    182 int main(){
    183     freopen("heoi2016_str.in","r",stdin);
    184     freopen("heoi2016_str.out","w",stdout);
    185     int i,j;
    186     n=read();m=read();
    187     scanf("%s",s);
    188     s[n]='a'-1;
    189     GetSA(sa,n+1,28);
    190     GetHT(n);
    191 //    Debug();
    192     for(i=1;i<=n;i++){//rank为时间轴,sa为下标 
    193         update(sa[i]+1,1,1,n,rot[i-1],rot[i]);
    194     }
    195     STT::ST_init();
    196     while(m--)solve();
    197     return 0;
    198 }

    解法3:后缀数组+暴力出奇迹

    建好后缀数组以后,对于每个询问,在rank数组上从询问点往前后方向暴力查找ht[]最大值!

    这么暴力居然在B站可以AC

      1 /*by SilverN*/
      2 #include<algorithm>
      3 #include<iostream>
      4 #include<cstring>
      5 #include<cstdio>
      6 #include<cmath>
      7 #include<vector>
      8 using namespace std;
      9 const int mxn=100010;
     10 int read(){
     11     int x=0,f=1;char ch=getchar();
     12     while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
     13     while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
     14     return x*f;
     15 }
     16 int sa[mxn],rk[mxn],ht[mxn];
     17 int wa[mxn],wb[mxn],wv[mxn],cnt[mxn];
     18 char s[mxn];
     19 inline int scmp(int *r,int a,int b,int l){
     20     return r[a]==r[b] && r[a+l]==r[b+l];
     21 }
     22 void GetSA(int *sa,int n,int m){
     23     int i,j,k;
     24     int *x=wa,*y=wb;
     25     for(i=0;i<m;i++)cnt[i]=0;
     26     for(i=0;i<n;i++)cnt[x[i]=s[i]-'a'+1]++;
     27     for(i=1;i<m;i++)cnt[i]+=cnt[i-1];
     28     for(i=n-1;i>=0;i--)sa[--cnt[x[i]]]=i;
     29 //    printf("fin
    ");
     30     for(int j=1,p=0;p<n;j<<=1,m=p){
     31         for(p=0,i=n-j;i<n;i++)y[p++]=i;
     32         for(i=0;i<n;i++)
     33             if(sa[i]>=j)y[p++]=sa[i]-j;
     34         for(i=0;i<n;i++)wv[i]=x[y[i]];
     35         for(i=0;i<m;i++)cnt[i]=0;
     36         for(i=0;i<n;i++)cnt[wv[i]]++;
     37         for(i=1;i<m;i++)cnt[i]+=cnt[i-1];
     38         for(i=n-1;i>=0;i--)sa[--cnt[wv[i]]]=y[i];
     39         swap(x,y);
     40         p=1;x[sa[0]]=0;
     41         for(i=1;i<n;i++)
     42             x[sa[i]]=scmp(y,sa[i],sa[i-1],j)?p-1:p++;
     43     }
     44 //    printf("fin
    ");
     45     return;
     46 }
     47 void GetHT(int n){
     48     int i,j,k=0;
     49     for(i=1;i<=n;i++)rk[sa[i]]=i;
     50     for(i=0;i<n;i++){
     51         if(k)k--;
     52 //        if(rk[i]==n)continue;
     53         j=sa[rk[i]-1];
     54         while(s[i+k]==s[j+k])k++;
     55         ht[rk[i]]=k;
     56     }
     57     return;
     58 }
     59 int n,m;
     60 void Debug(){
     61     int i;
     62     for(i=1;i<=n;i++)printf("%d ",sa[i]);puts("");
     63     for(i=0;i<n;i++)printf("%d ",rk[i]);puts("");
     64     for(i=1;i<=n;i++)printf("%d ",ht[i]);puts("");
     65     return;
     66 }
     67 int main(){
     68     freopen("heoi2016_str.in","r",stdin);
     69     freopen("heoi2016_str.out","w",stdout);
     70     int i,j;
     71     n=read();m=read();
     72     scanf("%s",s);
     73     s[n]='a'-1;
     74     GetSA(sa,n+1,28);
     75     GetHT(n);
     76 //    Debug();
     77     int a,b,c,d,ans;
     78     while(m--){
     79         a=read()-1;b=read()-1;c=read()-1;d=read()-1;
     80         if(a<=c && c<=b)ans=min(b-c+1,d-c+1);
     81         else ans=0;
     82 //        printf("ans:%d
    ",ans);
     83         int mini=d-c+1;
     84 //        int mid=(c>0)?rk[c]:1;
     85         int mid=rk[c];
     86         for(i=mid;i>1;i--){
     87 //            printf("mid:%d mini:%d
    ",mid,mini);
     88             if(mini<=ans)break;
     89             mini=min(mini,ht[i]);
     90             if(sa[i-1]>=a && sa[i-1]<=b)
     91                 ans=max(ans,min(mini,b-sa[i-1]+1));
     92         }
     93         mini=d-c+1;
     94         for(i=mid+1;i<=n;i++){
     95 //            printf("i:%d mini:%d
    ",i,mini);
     96             if(mini<=ans)break;
     97             mini=min(mini,ht[i]);
     98             if(sa[i]>=a && sa[i]<=b)
     99                 ans=max(ans,min(mini,b-sa[i]+1));
    100         }
    101         printf("%d
    ",ans);
    102     }
    103     return 0;
    104 }
  • 相关阅读:
    合数分解为质数的乘积模板
    P2626 斐波那契数列(升级版)(合数的质数分解, 大数为素数的概率十分小的利用)
    1305 Pairwise Sum and Divide
    1344 走格子 (前缀和)
    1347 旋转字符串
    E
    pek (北大oj)3070
    数学中各种矩阵收集(转至其他博主)
    (数论)逆元的线性算法
    洛谷P2627 修剪草坪 题解 单调队列优化DP
  • 原文地址:https://www.cnblogs.com/SilverNebula/p/6702216.html
Copyright © 2011-2022 走看看