Bzoj4556 [Tjoi2016&Heoi2016]字符串
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
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
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 }
本文为博主原创文章,转载请注明出处。