Bzoj4556 [Tjoi2016&Heoi2016]字符串

Posted SilverNebula

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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]字符串的主要内容,如果未能解决你的问题,请参考以下文章

Bzoj4556: [Tjoi2016&Heoi2016]字符串 后缀数组

[BZOJ4556][Tjoi2016&Heoi2016]字符串 后缀数组+主席树

[BZOJ4556][TJOI2016&&HEOI2016]字符串(二分答案+后缀数组+RMQ+主席树)

bzoj 4556: [Tjoi2016&Heoi2016]字符串

[BZOJ4556][TJOI2016&&HEOI2016]字符串

Bzoj4556 [Tjoi2016&Heoi2016]字符串