首页 > 代码库 > Bzoj4556 [Tjoi2016&Heoi2016]字符串

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\n",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\n",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\n"); 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\n"); 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\n",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\n",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\n",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\n",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\n",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\n"); 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\n"); 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\n",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\n",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\n",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\n",ans);102     }103     return 0;104 }

 

Bzoj4556 [Tjoi2016&Heoi2016]字符串