后缀数组14题题解

来源:互联网 发布:手机淘宝怎么推广 编辑:程序博客网 时间:2024/06/10 09:13

后缀数组14题小结:

http://poj.org/problem?id=1226

题目大意:给出n个字符串,找出一个最长的字符串,使得它本身或它的反串在所有字符串中都出现过,多组数据。

分析:水题一道,把所有的串正反接一遍,中间用没出现过的字符隔开,然后跑一遍后缀数组。最后二分答案+height分块,记录每一块是否覆盖所有串即可,O(n*log(n))。

    其实KMP也可以做,枚举其中最短的那个串的子串,跟其他所有串跑一遍KMP即可。

CODE(suffix array):

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<algorithm>#include<stdio.h>using namespace std;const int maxl=21000;const int maxn=110;const int cur=400;int s[maxl];int len;int cnt[maxl];int temp1[maxl],temp2[maxl];int *x,*y;int sa[maxl];int height[maxl];int id[maxl];bool vis[maxn];int t,n;int Comp(int *y,int a,int b,int l){return y[a]==y[b] && y[a+l]==y[b+l];}void Da(){int m=1000;x=temp1,y=temp2;for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<len; i++) cnt[ x[i]=s[i] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=len-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;int p=1;for (int j=1; p<len; j<<=1,m=p){p=0;for (int i=len-j; i<len; i++) y[ p++ ]=i;for (int i=0; i<len; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<len; i++) cnt[ x[ y[i] ] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=len-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];swap(x,y);p=1;x[ sa[0] ]=0;for (int i=1; i<len; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;}int k=0;height[ x[0] ]=0;for (int i=0; i<len; i++){if (k) k--;if (!x[i]) continue;int j=sa[ x[i]-1 ];while (s[i+k]==s[j+k]) k++;height[ x[i] ]=k;}/*for (int i=0; i<len; i++) printf("%d ",x[i]);printf("\n");for (int i=0; i<len; i++) printf("%d ",sa[i]);printf("\n");for (int i=0; i<len; i++) printf("%d ",height[i]);printf("\n");*/}bool Judge(int mid){for (int i=1; i<=n; i++) vis[i]=false;int num=0;int last=0;for (int i=0; i<len; i++){if (height[i]<mid){for (int j=last+1; j<=i; j++){vis[ id[ sa[j] ] ]=false;int k=sa[j-1];if (k>=0) vis[ id[k] ]=false;}num=0;last=i;}else{int isi=id[ sa[i] ];if ( !vis[isi] && isi ){vis[isi]=true;num++;}int j=sa[i-1];if (j>=0){if ( !vis[ id[j] ] && id[j] ){vis[ id[j] ]=true;num++;}}if (num>=n) return true;}}return false;}int Binary(){int L=0,R=200;while (L+1<R){int mid=(L+R)>>1;if ( Judge(mid) ) L=mid;else R=mid;}return L;}int main(){freopen("c.in","r",stdin);freopen("c.out","w",stdout);scanf("%d",&t);for (int q=1; q<=t; q++){scanf("%d",&n);getchar();len=0;for (int i=1; i<=n; i++){int L=len;char c=getchar();while ( c!='\n' ){s[ len ]=c;id[len]=i;s[ len++ ]+=cur;c=getchar();}int R=len-1;id[len]=0;s[ len++ ]=i+200;for (int j=R; j>=L; j--){id[len]=i;s[ len++ ]=s[j];}id[len]=0;s[ len++ ]=n-i;}/*for (int i=0; i<len; i++) printf("%d ",s[i]);printf("\n");for (int i=0; i<len; i++) printf("%d ",id[i]);printf("\n");*/Da();int ans=Binary();printf("%d\n",ans);}return 0;}

CODE(KMP):

#include<iostream>#include<cmath>#include<cstdio>#include<cstdlib>#include<algorithm>#include<stdio.h>using namespace std;const int maxn=110;char s[maxn][maxn];char r[maxn][maxn];char temp[maxn];int len[maxn];char ans[maxn];bool f[maxn];int Next[maxn];int t,n;int Ans;void Swap(char *p,char *q){for (int i=0; i<maxn; i++) temp[i]=p[i];for (int i=0; i<maxn; i++) p[i]=q[i];for (int i=0; i<maxn; i++) q[i]=temp[i];}void Preparation(){int min_s=0;int min_len=10000001;for (int i=1; i<=n; i++)if (len[i]<min_len){min_len=len[i];min_s=i;}Swap(s[1],s[min_s]);Swap(r[1],r[min_s]);swap(len[1],len[min_s]);}void Make_next(int L){Next[0]=Next[1]=0;int k=0;for (int i=2; i<=L; i++){while ( k && ans[k]!=ans[i-1] ) k=Next[k];if (ans[k]==ans[i-1]) k++;Next[i]=k;}}void Work(char *q,int L,int ans_len,bool *F){int k=0;for (int i=0; i<L; i++){while ( k && ans[k]!=q[i] ) k=Next[k];if ( ans[k]==q[i] ) k++;if (k==ans_len){*F=true;break;}}}void Do(char *q){for (int L=len[1]; L>=1; L--)for (int i=0; i<=len[1]-L; i++){for (int j=0; j<L; j++) ans[j]=q[i+j];Make_next(L);int j;for (j=2; j<=n; j++){f[j]=false;Work(s[j],len[j],L,&f[j]);if (!f[j]) Work(r[j],len[j],L,&f[j]);if (!f[j]) break;}if (j!=n+1) continue;Ans=max(Ans,L);break;}}int main(){freopen("c.in","r",stdin);freopen("c.out","w",stdout);scanf("%d",&t);for (int q=1; q<=t; q++){Ans=0;scanf("%d",&n);for (int i=1; i<=n; i++){scanf("%s",&s[i]);len[i]=strlen(s[i]);for (int j=0; j<len[i]; j++) r[i][j]=s[i][len[i]-j-1];}Preparation();Do(s[1]);Do(r[1]);printf("%d\n",Ans);}return 0;}

http://poj.org/problem?id=1743

题目大意:给出一个串,求其中最长的一个子串,使得它本身或它每一位+x在原串中出现了两次以上,并且不重叠。

分析:水题一道,求出每两位之间的差,跑后缀数组,二分答案+height分块,每一块看sa的最大值减最小值是否>=len即可。

CODE:

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<algorithm>using namespace std;const int maxn=20110;int a[maxn];int cnt[maxn];int temp1[maxn],temp2[maxn];int *x=temp1,*y=temp2;int sa[maxn];int height[maxn];int n;int Comp(int *r,int a,int b,int l){return r[a]==r[b] && r[a+l]==r[b+l];}void Da(){int m=200;for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<n; i++) cnt[ x[i]=a[i] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=n-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;int p=1;for (int j=1; p<n; j<<=1,m=p){p=0;for (int i=n-j; i<n; i++) y[ p++ ]=i;for (int i=0; i<n; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<n; i++) cnt[ x[ y[i] ] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=n-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];p=1;swap(x,y);x[ sa[0] ]=0;for (int i=1; i<n; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;}}void Calc_height(){height[0]=0;int k=0;for (int i=0; i<n; i++){if (k) k--;if (!x[i]) continue;int j=sa[ x[i]-1 ];while (a[i+k]==a[j+k]) k++;height[ x[i] ]=k;}}bool Judge(int z){int low,high;for (int i=0; i<n; i++){if (height[i]<z){low=20001;high=0;}else{low=min(low,sa[i]);high=max(high,sa[i]);low=min(low,sa[i-1]);high=max(high,sa[i-1]);if (low+z<high) return true;}}return false;}int Binary(){int L=0,R=20001;while (L+1<R){int mid=(L+R)>>1;if ( Judge(mid) ) L=mid;else R=mid;}return L;}int main(){freopen("c.in","r",stdin);freopen("my.out","w",stdout);while ( 1 ){scanf("%d",&n);if (!n) break;for (int i=0; i<n; i++) scanf("%d",&a[i]);for (int i=1; i<n; i++) a[i-1]=a[i]-a[i-1];for (int i=0; i<n; i++) a[i]+=100;a[n-1]=0;//for (int i=0; i<n; i++) printf("%d ",a[i]);//printf("\n");Da();Calc_height();int ans=Binary();if (ans>=4) printf("%d\n",ans+1);else printf("0\n");//for (int i=0; i<n; i++) printf("%d ",sa[i]);//printf("\n");//for (int i=0; i<n; i++) printf("%d ",height[i]);//printf("\n");}return 0;}

http://poj.org/problem?id=2406

题目大意:给一个字符串,求一个最大的t,使得原串是某个串刚好复制t次得到的。

分析:先讲KMP的做法:若n-next[n]是n的因数则为n/(n-next[n]),否则为1。这个很好证明。

至于后缀数组,本质上也差不多,就是求一个最小的k使得lcp(suffix(0),suffix(k))==n-k。

CODE(KMP):

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=1000100;int Next[maxn];char s[maxn];int len;int main(){freopen("c.in","r",stdin);freopen("c.out","w",stdout);while ( scanf("%s",&s),s[0]!='.' ){len=strlen(s);int k=0;Next[0]=Next[1]=0;for (int i=2; i<=len; i++){while ( k && s[k]!=s[i-1] ) k=Next[k];if ( s[k]==s[i-1] ) k++;Next[i]=k;}//for (int i=0; i<=len; i++) printf("%d ",Next[i]);//printf("\n");int ans=1;if ( len%(len-Next[len])==0 ) ans=len/(len-Next[len]);printf("%d\n",ans);}return 0;}

CODE(suffix array)(我超时了,因为我不会DC3……):

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=1000100;char s[maxn];int a[maxn];int cnt[maxn];int temp1[maxn],temp2[maxn];int *x=temp1,*y=temp2;int sa[maxn];int height[maxn];bool f[maxn];int num;int Comp(int *r,int a,int b,int l){return r[a]==r[b] && r[a+l]==r[b+l];}void Da(int n){int m=30;for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<n; i++) cnt[ x[i]=a[i] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=n-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;int p=1;for (int j=1; p<n; j<<=1,m=p){p=0;for (int i=n-j; i<n; i++) y[ p++ ]=i;for (int i=0; i<n; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<n; i++) cnt[ x[ y[i] ] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=n-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];p=1;swap(x,y);x[ sa[0] ]=0;for (int i=1; i<n; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;}}void Calc_height(int n){height[0]=0;int k=0;for (int i=0; i<n; i++){if (k) k--;if (!x[i]) continue;int j=sa[ x[i]-1 ];while (a[i+k]==a[j+k]) k++;height[ x[i] ]=k;}}int main(){freopen("c.in","r",stdin);freopen("c.out","w",stdout);while ( scanf("%s",&s) && s[0]!='.' ){int len=strlen(s);for (int i=0; i<len; i++) a[i]=s[i]-'a'+1;a[ len++ ]=0;Da(len);Calc_height(len);len--;int min_k=len,lcp=len;for (int i=x[0]+1; i<=len; i++){lcp=min(lcp,height[i]);int k=sa[i];if ( lcp==len-k && len%k==0 ) min_k=min(min_k,k);}int ans=len/min_k;min_k=len,lcp=len;for (int i=x[0]; i>=0; i--){lcp=min(lcp,height[i]);int k=sa[i-1];if ( lcp==len-k && len%k==0 ) min_k=min(min_k,k);}ans=max(ans,len/min_k);printf("%d\n",ans);}return 0;}

http://poj.org/problem?id=2774

题目大意:两个串的最长公共子串。

分析:裸题一道……

CODE:

#include<iostream>#include<string>#include<cstring>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=200100;char s[maxn];int a[maxn];int id[maxn];int cnt[maxn];int temp1[maxn],temp2[maxn];int *x=temp1,*y=temp2;int sa[maxn];int height[maxn];int Comp(int *r,int a,int b,int l){return r[a]==r[b] && r[a+l]==r[b+l];}void Da(int n,int m){for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<n; i++) cnt[ x[i]=a[i] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=n-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;int p=1;for (int j=1; p<n; j<<=1,m=p){p=0;for (int i=n-j; i<n; i++) y[ p++ ]=i;for (int i=0; i<n; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<n; i++) cnt[ x[ y[i] ] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=n-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];p=1;swap(x,y);x[ sa[0] ]=0;for (int i=1; i<n; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;}}void Calc_height(int n){height[0]=0;int k=0;for (int i=0; i<n; i++){if (k) k--;if (!x[i]) continue;int j=sa[ x[i]-1 ];while (a[i+k]==a[j+k]) k++;height[ x[i] ]=k;}}int main(){freopen("c.in","r",stdin);freopen("c.out","w",stdout);scanf("%s",&s);int len1=strlen(s);scanf("%s",&s[len1]);int len2=strlen(s);for (int i=0; i<len1; i++) a[i]=s[i]-'a'+2;a[len1]=1;for (int i=len1; i<len2; i++) a[i+1]=s[i]-'a'+2;a[len2+1]=0;len2+=2;Da(len2,35);Calc_height(len2);for (int i=0; i<len1; i++) id[i]=1;id[len1]=4;for (int i=len1+1; i<len2-1; i++) id[i]=2;id[len2-1]=5;int ans=0;for (int i=1; i<len2; i++)if ( id[ sa[i-1] ]+id[ sa[i] ]==3 )ans=max(ans,height[i]);printf("%d\n",ans);return 0;}

http://poj.org/problem?id=3261

题目大意:求一个最大的k,使得某个子串在原串中出现了k次以上,可重叠。

分析:二分答案,看height数组中是否有>=k的一段height都大于了当前答案。

但我们发现可以枚举任意一个长度为k的区间,用他的最小值去更新答案(很明显,长度更长一定不会优),这是滑动区间最小值问题,单调队列即可。

CODE:

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=20100;const int maxm=1000100;int a[maxn];int cnt[maxm];int temp1[maxn],temp2[maxn];int *x=temp1,*y=temp2;int sa[maxn];int height[maxn];struct data{int val,Time;} que[maxn];int head=1,tail=0;int n,K;int m=1000010;int ans;int Comp(int *r,int a,int b,int l){return r[a]==r[b] && r[a+l]==r[b+l];}void Da(){for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<n; i++) cnt[ x[i]=a[i] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=n-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;int p=1;for (int j=1; p<n; j<<=1,m=p){p=0;for (int i=n-j; i<n; i++) y[ p++ ]=i;for (int i=0; i<n; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<n; i++) cnt[ x[ y[i] ] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=n-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];p=1;swap(x,y);x[ sa[0] ]=0;for (int i=1; i<n; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;}}void Calc_height(){height[0]=0;int k=0;for (int i=0; i<n; i++){if (k) k--;if (!x[i]) continue;int j=sa[ x[i]-1 ];while (a[i+k]==a[j+k]) k++;height[ x[i] ]=k;}}void Solve(){for (int i=0; i<K; i++){que[ ++tail ].val=height[i];que[tail].Time=i;while ( tail>1 && que[tail-1].val>que[tail].val ){tail--;que[tail]=que[tail+1];}}ans=que[head].val;for (int i=K; i<n; i++){que[ ++tail ].val=height[i];que[tail].Time=i;while ( tail>head && que[tail-1].val>que[tail].val ){tail--;que[tail]=que[tail+1];}if (que[head].Time<=i-K) head++;ans=max(ans,que[head].val);}}void Print(int *r){for (int i=0; i<n; i++) printf("%d ",r[i]);printf("\n");}int main(){freopen("c.in","r",stdin);freopen("c.out","w",stdout);scanf("%d%d",&n,&K);if (K==1){printf("%d\n",n);return 0;}for (int i=0; i<n; i++){scanf("%d",&a[i]);a[i]++;}a[ n++ ]=0;Da();Calc_height();//Print(x);//Print(sa);//Print(height);K--;Solve();printf("%d\n",ans);return 0;}

http://poj.org/problem?id=3294

题目大意:多个串的最长公共子串,并输出所有串。

分析:本题其实分成了两个任务:求长度+打印答案。前者用二分答案,后者单独做一遍即可。

CODE:

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=101000;int a[maxn];char s[1010];int cnt[maxn];int temp1[maxn],temp2[maxn];int *x=temp1,*y=temp2;int sa[maxn];int height[maxn];int id[maxn];bool f[maxn];int N,n,m;int Comp(int *r,int a,int b,int l){return r[a]==r[b] && r[a+l]==r[b+l];}void Da(){m=500;for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<n; i++) cnt[ x[i]=a[i] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=n-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;int p=1;for (int j=1; p<n; j<<=1,m=p){p=0;for (int i=n-j; i<n; i++) y[ p++ ]=i;for (int i=0; i<n; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<n; i++) cnt[ x[ y[i] ] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=n-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];p=1;swap(x,y);x[ sa[0] ]=0;for (int i=1; i<n; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;}}void Calc_height(){height[0]=0;int k=0;for (int i=0; i<n; i++){if (k) k--;if (!x[i]) continue;int j=sa[ x[i]-1 ];while (a[i+k]==a[j+k]) k++;height[ x[i] ]=k;}}bool Judge(int mid){for (int i=0; i<=N; i++) f[i]=false;int num=0;int last=0;for (int i=1; i<n; i++)if (height[i]<mid){for (int j=last+1; j<i; j++) f[ id[ sa[j] ] ]=f[ id[ sa[j-1] ] ]=false;last=i;num=0;}else{int isi=id[ sa[i] ];int isi1=id[ sa[i-1] ];if ( isi && !f[isi] ){f[isi]=true;num++;}if ( isi1 && !f[isi1] ){f[isi1]=true;num++;}if (2*num>N) return true;}return false;}int Binary(){int L=0,R=1001;while (L+1<R){int mid=(L+R)>>1;if ( Judge(mid) ) L=mid;else R=mid;}return L;}void Print(int len){for (int i=0; i<=N; i++) f[i]=false;int num=0;int last=0;height[n]=0;for (int i=1; i<=n; i++)if (height[i]<len){if (2*num>N){for (int j=0; j<len; j++) printf("%c",a[ sa[i-1]+j ]-200);printf("\n");}for (int j=last+1; j<i; j++) f[ id[ sa[j-1] ] ]=f[ id[ sa[j] ] ]=false;last=i;num=0;}else{int isi=id[ sa[i] ];int isi1=id[ sa[i-1] ];if ( isi && !f[isi] ){f[isi]=true;num++;}if ( isi1 && !f[isi1] ){f[isi1]=true;num++;}}}int main(){freopen("c.in","r",stdin);freopen("c.out","w",stdout);while ( scanf("%d",&N),N ){n=0;for (int i=1; i<=N; i++){scanf("%s",&s);if (N==1){printf("%s\n",s);continue;}int len=strlen(s);for (int j=0; j<len; j++){a[n+j]=s[j];a[n+j]+=200;id[n+j]=i;}n+=len;a[n]=i;id[ n++ ]=0;}a[n-1]=0;//for (int i=0; i<n; i++) printf("%d %d\n",a[i],id[i]);//printf("\n");Da();Calc_height();int len=Binary();if (N!=1) if (len) Print(len);else printf("?\n");printf("\n");}return 0;}

http://poj.org/problem?id=3415

题目大意:给出两个串A,B,求有多少个三元组(I,j,k)使得A从i开始的k位==B从j开始的k位,其中k要大于一个给定的数值x,多组数据。

分析:跑一遍后缀数组,然后我们在做到sa[i]的时候,我们要考虑它和sa[j](1<=j<I且sa[i],sa[j]来自不同的字符串)对答案的贡献。其中k可以取x~lcp(sa[i],sa[j])。

一开始我用了两棵线段树,3000ms,后来发现简直就是个傻叉。由于一个height[i]进栈会不断地把前面那些比它大的值压成和它自己一样的值,我们再分别记录一下当前栈中来自A,B串的总和是多少即可,900ms。

CODE(单调栈):

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=200100;typedef long long LL;char s[maxn];int a[maxn];int id[maxn];int cnt[maxn];int temp1[maxn],temp2[maxn];int *x=temp1,*y=temp2;int sa[maxn];int height[maxn];struct data{LL val;int num[2];} sak[maxn];int tail;LL sum[2];int k,n;LL ans;int Comp(int *r,int a,int b,int l){return r[a]==r[b] && r[a+l]==r[b+l];}void Da(int m){for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<n; i++) cnt[ x[i]=a[i] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=n-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;int p=1;for (int j=1; p<n; j<<=1,m=p){p=0;for (int i=n-j; i<n; i++) y[ p++ ]=i;for (int i=0; i<n; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<n; i++) cnt[ x[ y[i] ] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=n-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];p=1;swap(x,y);x[ sa[0] ]=0;for (int i=1; i<n; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;}}void Calc_height(){height[0]=0;int k=0;for (int i=0; i<n; i++){if (k) k--;if (!x[i]) continue;int j=sa[ x[i]-1 ];while (a[i+k]==a[j+k]) k++;height[ x[i] ]=k;}}void Solve(){ans=0;sum[0]=sum[1]=0;tail=0;for (int i=2; i<n; i++){int v=height[i]-k+1;sak[ ++tail ].val=v;int isi=id[ sa[i-1] ];sum[isi]+=v;sak[tail].num[isi]=1;sak[tail].num[!isi]=0;while ( tail>1 && sak[tail-1].val>=sak[tail].val ){sum[0]-=sak[tail-1].num[0]*(sak[tail-1].val-v);sum[1]-=sak[tail-1].num[1]*(sak[tail-1].val-v);sak[tail].num[0]+=sak[tail-1].num[0];sak[tail].num[1]+=sak[tail-1].num[1];sak[--tail]=sak[tail+1];}if (v<=0){sum[0]-=v*sak[tail].num[0];sum[1]-=v*sak[tail].num[1];tail--;}ans+=sum[ !id[ sa[i] ] ];}cout<<ans<<endl;}int main(){freopen("c.in","r",stdin);freopen("c.out","w",stdout);while ( scanf("%d",&k),k ){scanf("%s",&s);n=strlen(s);for (int i=0; i<n; i++){a[i]=s[i];a[i]+=100;id[i]=0;}a[n]=499;id[ n++ ]=3;scanf("%s",&s);int len=strlen(s);for (int i=0; i<len; i++){a[n+i]=s[i];a[n+i]+=100;id[n+i]=1;}n+=len;a[n]=0;id[ n++ ]=4;Da(500);Calc_height();Solve();}return 0;}

CODE(线段树):

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=200100;typedef long long LL;char s[maxn];int a[maxn];int id[maxn];int cnt[maxn];int temp1[maxn],temp2[maxn];int *x=temp1,*y=temp2;int sa[maxn];int height[maxn];struct Tnode{LL sum;int add;bool empty;} tree[2][maxn<<2];int k,n;LL ans;int Comp(int *r,int a,int b,int l){return r[a]==r[b] && r[a+l]==r[b+l];}void Da(int m){for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<n; i++) cnt[ x[i]=a[i] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=n-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;int p=1;for (int j=1; p<n; j<<=1,m=p){p=0;for (int i=n-j; i<n; i++) y[ p++ ]=i;for (int i=0; i<n; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<n; i++) cnt[ x[ y[i] ] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=n-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];p=1;swap(x,y);x[ sa[0] ]=0;for (int i=1; i<n; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;}}void Calc_height(){height[0]=0;int k=0;for (int i=0; i<n; i++){if (k) k--;if (!x[i]) continue;int j=sa[ x[i]-1 ];while (a[i+k]==a[j+k]) k++;height[ x[i] ]=k;}}void Build(int root,int L,int R){tree[0][root].sum=tree[0][root].add=0;tree[0][root].empty=false;tree[1][root].sum=tree[1][root].add=0;tree[1][root].empty=false;if (L==R) return;int left=root<<1;int right=left|1;int mid=(L+R)>>1;Build(left,L,mid);Build(right,mid+1,R);}void Down(int Id,int root,int L,int R,int left,int right,int mid){if (tree[Id][root].empty){tree[Id][left].sum=tree[Id][left].add=0;tree[Id][right].sum=tree[Id][right].add=0;tree[Id][left].empty=tree[Id][right].empty=true;tree[Id][root].empty=false;}if (tree[Id][root].add){tree[Id][left].add+=tree[Id][root].add;tree[Id][left].sum+=(long long)tree[Id][root].add*(mid-L+1);tree[Id][right].add+=tree[Id][root].add;tree[Id][right].sum+=(long long)tree[Id][root].add*(R-mid);tree[Id][root].add=0;}}void Update(int Id,int root,int L,int R,int x,int y){if ( y<L || R<x ) return;if ( x<=L && R<=y ){tree[Id][root].add++;tree[Id][root].sum+=(R-L+1);return;}int left=root<<1;int right=left|1;int mid=(L+R)>>1;Down(Id,root,L,R,left,right,mid);Update(Id,left,L,mid,x,y);Update(Id,right,mid+1,R,x,y);tree[Id][root].sum=tree[Id][left].sum+tree[Id][right].sum;}LL Query(int Id,int root,int L,int R,int x,int y){if ( y<L || R<x ) return 0;if ( x<=L && R<=y ) return tree[Id][root].sum;int left=root<<1;int right=left|1;int mid=(L+R)>>1;Down(Id,root,L,R,left,right,mid);LL vl=Query(Id,left,L,mid,x,y);LL vr=Query(Id,right,mid+1,R,x,y);return vl+vr;}void Clear(int root,int L,int R,int x,int y){if ( y<L || R<x ) return;if ( x<=L && R<=y ){tree[0][root].sum=tree[0][root].add=0;tree[0][root].empty=true;tree[1][root].sum=tree[1][root].add=0;tree[1][root].empty=true;return;}int left=root<<1;int right=left|1;int mid=(L+R)>>1;Down(0,root,L,R,left,right,mid);Down(1,root,L,R,left,right,mid);Clear(left,L,mid,x,y);Clear(right,mid+1,R,x,y);tree[0][root].sum=tree[0][left].sum+tree[0][right].sum;tree[1][root].sum=tree[1][left].sum+tree[1][right].sum;}void Solve(){int N=k;for (int i=0; i<n; i++) N=max(N,height[i]+1);Build(1,1,N);LL ans=0;for (int i=1; i<n; i++){Clear(1,1,N,height[i]+1,N);int isi=id[ sa[i] ];if (height[i]>=k) ans+=Query(!isi,1,1,N,k,height[i]);if ( isi==0 || isi==1 ) Update(isi,1,1,N,k,N);//for (int j=1; j<=N; j++) cout<<Query(1,1,1,N,j,j)<<' ';//printf("\n");}cout<<ans<<endl;}int main(){freopen("c.in","r",stdin);freopen("c.out","w",stdout);while ( scanf("%d",&k),k ){scanf("%s",&s);n=strlen(s);for (int i=0; i<n; i++){a[i]=s[i];a[i]+=100;id[i]=0;}a[n]=50;id[ n++ ]=3;scanf("%s",&s);int len=strlen(s);for (int i=0; i<len; i++){a[n+i]=s[i];a[n+i]+=100;id[n+i]=1;}n+=len;a[n]=0;id[ n++ ]=4;Da(500);Calc_height();Solve();}return 0;}

http://poj.org/problem?id=3693

题目大意:给出一个串,求它的一个子串,使得这个子串是串t刚好重复k次拼成的,且k最大。如果有多个串输出字典序最小,多组数据。

分析:这题要输出字典序最小好恶心。我们要跑两个后缀数组:一个是正串的,一个是反串的。我们枚举t的长度L,然后检验s[i*L]与s[i*L+L]前后最长能匹配多少,假设最前能匹配到head,长度为len,我们的开头就可以是head~head+(len%L)这段,再用一个rmq求这段sa的最小值即可。In short:两个后缀数组,三个rmq。

CODE:

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=100100;const int maxl=20;char s[maxn];int a1[maxn];int a2[maxn];int cnt[maxn];int temp1[maxn],temp2[maxn];int *x=temp1,*y=temp2;int Sa1[maxn],Sa2[maxn];int Rank1[maxn],Rank2[maxn];int Height1[maxn],Height2[maxn];int rmq1[maxn][maxl],rmq2[maxn][maxl],rmq[maxn][maxl];int P[maxn];int n,Num=0;void Make_P(){P[1]=0;for (int i=2; i<maxn; i++) P[i]=P[i/2]+1;}int Comp(int *r,int a,int b,int l){return r[a]==r[b] && r[a+l]==r[b+l];}void Da(int *sa,int *rank,int *a,int *height,int m){for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<n; i++) cnt[ x[i]=a[i] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=n-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;int p=1;for (int j=1; p<n; j<<=1,m=p){p=0;for (int i=n-j; i<n; i++) y[ p++ ]=i;for (int i=0; i<n; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<n; i++) cnt[ x[ y[i] ] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=n-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];p=1;swap(x,y);x[ sa[0] ]=0;for (int i=1; i<n; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;}for (int i=0; i<n; i++) rank[ sa[i] ]=i;height[0]=0;int k=0;for (int i=0; i<n; i++){if (k) k--;if (!rank[i]) continue;int j=sa[ rank[i]-1 ];while (a[i+k]==a[j+k]) k++;height[ rank[i] ]=k;}}void Make_rmq(){for (int i=0; i<n; i++){rmq1[i][0]=Height1[i];rmq2[i][0]=Height2[i];rmq[i][0]=i;}int maxj=P[n]+1;for (int j=1; j<maxj; j++)for (int i=0; i<n; i++){int mid=i+(1<<(j-1));mid=min(mid,n-1);rmq1[i][j]=min(rmq1[i][j-1],rmq1[mid][j-1]);rmq2[i][j]=min(rmq2[i][j-1],rmq2[mid][j-1]);rmq[i][j]=rmq[i][j-1];if ( Rank1[ rmq[mid][j-1] ]<Rank1[ rmq[i][j] ] ) rmq[i][j]=rmq[mid][j-1];}}int Query1(int i,int j){int p=Rank1[i],q=Rank1[j];if (p>q) swap(p,q);p++;int lg=P[q-p+1];q=q-(1<<lg)+1;int len=min(rmq1[p][lg],rmq1[q][lg]);return len;}int Query2(int i,int j){int p=Rank2[i],q=Rank2[j];if (p>q) swap(p,q);p++;int lg=P[q-p+1];q=q-(1<<lg)+1;int len=min(rmq2[p][lg],rmq2[q][lg]);return len;}int Query(int p,int q){int lg=P[q-p+1];q=q-(1<<lg)+1;int h=rmq[p][lg];if ( Rank1[ rmq[q][lg] ]<Rank1[h] ) h=rmq[q][lg];return h;}void Solve(){int ans=1,ansh=0,ansL=0;//ans==1 special judgefor (int L=1; L<n-1; L++)for (int i=0; i<n; i+=L){int j=i+L;if (j>=n-1) continue;int len1=Query1(i,j);int len2=Query2(n-i-1,n-j-1);int len=len1+len2;int h=i-len2;int na=len/L+1;int nh=Query(h,h+len%L);if ( na>ans || ( na==ans && Rank1[nh]<Rank1[ansh] ) ){ans=na;ansh=nh;ansL=L;}}if (ans>1)for (int i=1; i<=ans; i++)for (int j=0; j<ansL; j++)printf("%c",s[ansh+j]);else{int c='z';for (int i=0; i<n-1; i++) c=min(c,(int)s[i]);printf("%c",c);}printf("\n");}int main(){freopen("c.in","r",stdin);freopen("c.out","w",stdout);Make_P();while ( scanf("%s",&s),s[0]!='#' ){Num++;printf("Case %d: ",Num);n=strlen(s);for (int i=0; i<n; i++) a1[i]=s[i]-'a'+1;for (int i=0; i<n; i++) a2[i]=a1[n-i-1];a1[n]=0;a2[ n++ ]=0;Da(Sa1,Rank1,a1,Height1,27);Da(Sa2,Rank2,a2,Height2,27);Make_rmq();Solve();}return 0;}

http://www.spoj.com/problems/PHRASES/

题目大意:给出n个串,求一个最长的串,使得它在每个串中不重叠地出现了至少2次:

分析:跟poj3294与poj1743差不多,水……

CODE:

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=100100;const int oo=100000001;char s[10010];int a[maxn];int id[maxn];int cnt[maxn];int temp1[maxn],temp2[maxn];int *x=temp1,*y=temp2;int sa[maxn];int height[maxn];int low[maxn];int high[maxn];int t,N,n,m;int Comp(int *r,int p,int q,int l){return r[p]==r[q] && r[p+l]==r[q+l];}void Da(){for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<n; i++) cnt[ x[i]=a[i] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=n-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;int p=1;for (int j=1; p<n; j<<=1,m=p){p=0;for (int i=n-j; i<n; i++) y[ p++ ]=i;for (int i=0; i<n; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<n; i++) cnt[ x[ y[i] ] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=n-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];p=1;swap(x,y);x[ sa[0] ]=0;for (int i=1; i<n; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;}}void Calc_height(){height[0]=0;int k=0;for (int i=0; i<n; i++){if (k) k--;if (!x[i]) continue;int j=sa[ x[i]-1 ];while (a[i+k]==a[j+k]) k++;height[ x[i] ]=k;}}bool Judge(int mid){for (int i=1; i<=N; i++){low[i]=oo;high[i]=-oo;}int last=0,num=0;for (int i=1; i<n; i++){if (height[i]<mid){for (int j=last+1; j<i; j++){int temp=id[ sa[j-1] ];low[temp]=oo;high[temp]=-oo;temp=id[ sa[j] ];low[temp]=oo;high[temp]=-oo;}last=i;num=0;}else{int temp=id[ sa[i-1] ];if ( high[temp]-low[temp]<mid && temp ){high[temp]=max(high[temp],sa[i-1]);low[temp]=min(low[temp],sa[i-1]);if (high[temp]-low[temp]>=mid) num++;}temp=id[ sa[i] ];if ( high[temp]-low[temp]<mid && temp ){high[temp]=max(high[temp],sa[i]);low[temp]=min(low[temp],sa[i]);if (high[temp]-low[temp]>=mid) num++;}if (num>=N) return true;}}return false;}int Binary(){int L=0,R=maxn;while (L+1<R){int mid=(L+R)>>1;if ( Judge(mid) ) L=mid;else R=mid;}return L;}int main(){freopen("c.in","r",stdin);freopen("my.out","w",stdout);scanf("%d",&t);while (t--){scanf("%d",&N);n=0;for (int i=1; i<=N; i++){scanf("%s",&s);int len=strlen(s);while (s[len-1]==' ') len--;for (int j=0; j<len; j++){a[n+j]=(int)s[j]+50;id[n+j]=i;}n+=len;a[n]=i;id[ n++ ]=i+50;}a[n-1]=0;m=500;Da();Calc_height();int ans=Binary();printf("%d\n",ans);}return 0;}

http://www.spoj.com/problems/REPEATS/

题目大意:跟poj3693一样,不过输出k即可。

分析:这题是poj3693的简化版,不过我们可以考虑一些更简单的做法。我们求出s[i*L]与s[i*L+L]往后能匹配len个之后,我们可以认为它是在前面少了L-len%L个字符,然后我们把开头挪到那里去再搞一次尝试更新答案即可。

CODE:

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=50100;const int maxl=20;int a[maxn];int cnt[maxn];int temp1[maxn],temp2[maxn];int *x=temp1,*y=temp2;int sa[maxn];int height[maxn];int lg[maxn];int rmq[maxn][maxl];int t,n,m;void Make_lg(){lg[1]=0;for (int i=2; i<maxn; i++) lg[i]=lg[i/2]+1;}int Comp(int *r,int p,int q,int l){return r[p]==r[q] && r[p+l]==r[q+l];}void Da(){for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<n; i++) cnt[ x[i]=a[i] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=n-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;int p=1;for (int j=1; p<n; j<<=1,m=p){p=0;for (int i=n-j; i<n; i++) y[ p++ ]=i;for (int i=0; i<n; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<n; i++) cnt[ x[ y[i] ] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=n-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];p=1;swap(x,y);x[ sa[0] ]=0;for (int i=1; i<n; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;}}void Calc_height(){height[0]=0;int k=0;for (int i=0; i<n; i++){if (k) k--;if (!x[i]) continue;int j=sa[ x[i]-1 ];while (a[i+k]==a[j+k]) k++;height[ x[i] ]=k;}}void Make_rmq(){for (int i=0; i<n; i++) rmq[i][0]=height[i];for (int j=1; j<=lg[n]; j++)for (int i=0; i<n; i++){int mid=i+(1<<(j-1));mid=min(mid,n-1);rmq[i][j]=min(rmq[i][j-1],rmq[mid][j-1]);}}int Query(int i,int j){int p=x[i],q=x[j];if (p>q) swap(p,q);p++;int r=lg[q-p+1];q=q-(1<<r)+1;return min(rmq[p][r],rmq[q][r]);}void Solve(){int ans=1;for (int L=1; L<n-1; L++)for (int i=0; i<n; i+=L){int j=i+L;if (j>=n) break;int len=Query(i,j);ans=max(ans,len/L+1);int ni=i-(L-len%L);if (ni<0) continue;j=ni+L;len=Query(ni,j);ans=max(ans,len/L+1);}printf("%d\n",ans);}int main(){freopen("c.in","r",stdin);freopen("c.out","w",stdout);Make_lg();scanf("%d",&t);while (t--){scanf("%d",&n);for (int i=0; i<n; i++){char c=getchar();while ( c!='a' && c!='b' ) c=getchar();a[i]=c-'a'+1;}a[ n++ ]=0;m=3;Da();Calc_height();Make_rmq();Solve();}return 0;}

http://www.spoj.com/problems/DISUBSTR/

题目大意:给出一个串,求有多少个不同的子串。

分析:水题,用len(sa[i])-height[i]更新答案即可(前提是len(sa[i])>height[i])。

CODE:

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=1010;char s[maxn];int a[maxn];int cnt[maxn];int temp1[maxn],temp2[maxn];int *x=temp1,*y=temp2;int sa[maxn];int height[maxn];int id[maxn];int N,n,m;int Comp(int *r,int a,int b,int l){return r[a]==r[b] && r[a+l]==r[b+l];}void Da(){for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<n; i++) cnt[ x[i]=a[i] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=n-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;int p=1;for (int j=1; p<n; j<<=1,m=p){p=0;for (int i=n-j; i<n; i++) y[ p++ ]=i;for (int i=0; i<n; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<n; i++) cnt[ x[ y[i] ] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=n-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];p=1;swap(x,y);x[ sa[0] ]=0;for (int i=1; i<n; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;}}void Calc_height(){height[0]=0;int k=0;for (int i=0; i<n; i++){if (k) k--;if (!x[i]) continue;int j=sa[ x[i]-1 ];while (a[i+k]==a[j+k]) k++;height[ x[i] ]=k;}}void Solve(){for (int i=0; i<n; i++) id[i]=0;int ans=0,sum=0;for (int i=n-2; i>=0; i--){sum++;sum-=id[i+1];ans+=sum;id[ height[ x[n-i-2] ] ]++;}printf("%d\n",ans);}int main(){freopen("c.in","r",stdin);freopen("c.out","w",stdout);scanf("%d",&N);while (N--){scanf("%s",&s);n=strlen(s);for (int i=0; i<n; i++) a[i]=(int)s[i]+1;a[ n++ ]=0;m=500;Da();Calc_height();Solve();}return 0;}

http://www.spoj.com/problems/SUBST1/

和上面那题一毛一样……

 

Timus Online Judge 1517:

题目大意:两个串的最长公共子串,并输出字典序最小。

分析:水……

CODE:

#include<iostream>#include<string>#include<cstring>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=200100;char s[maxn];int a[maxn];int id[maxn];int cnt[maxn];int temp1[maxn],temp2[maxn];int *x=temp1,*y=temp2;int sa[maxn];int height[maxn];int Comp(int *r,int a,int b,int l){return r[a]==r[b] && r[a+l]==r[b+l];}void Da(int n,int m){for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<n; i++) cnt[ x[i]=a[i] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=n-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;int p=1;for (int j=1; p<n; j<<=1,m=p){p=0;for (int i=n-j; i<n; i++) y[ p++ ]=i;for (int i=0; i<n; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<n; i++) cnt[ x[ y[i] ] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=n-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];p=1;swap(x,y);x[ sa[0] ]=0;for (int i=1; i<n; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;}}void Calc_height(int n){height[0]=0;int k=0;for (int i=0; i<n; i++){if (k) k--;if (!x[i]) continue;int j=sa[ x[i]-1 ];while (a[i+k]==a[j+k]) k++;height[ x[i] ]=k;}}int main(){freopen("c.in","r",stdin);freopen("c.out","w",stdout);int n;scanf("%d",&n);scanf("%s",&s);scanf("%s",&s[n]);for (int i=0; i<n; i++){a[i]=(int)s[i]+1;id[i]=1;}for (int i=0; i<n; i++){a[n+1+i]=(int)s[n+i]+1;id[n+1+i]=2;}a[n]=499;id[n]=3;n=n*2+2;a[n-1]=0;id[n-1]=4;Da(n,500);Calc_height(n);int ans=0,ansid=0;for (int i=1; i<n; i++)if ( id[ sa[i-1] ]+id[ sa[i] ]==3 && height[i]>=ans ){ans=height[i];ansid=sa[i];}for (int i=0; i<ans; i++) printf("%c",a[ansid+i]-1);printf("\n");return 0;}

Timus Online Judge 1297:

题目大意:求原串的最长回文子串,相同长度则输出最前面一个。

分析:本人是用两次二分答案(由于单调性有点特殊),一次二分奇数长度,一次二分偶数。

其实可以正反串接起来做一遍后缀数组,然后枚举中间点。

CODE:

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=2010;char s[maxn];int a[maxn];int id[maxn];int cnt[maxn];int temp1[maxn],temp2[maxn];int *x=temp1,*y=temp2;int sa[maxn];int height[maxn];bool h[maxn];bool t[maxn];int n,m;int Comp(int *r,int p,int q,int l){return r[p]==r[q] && r[p+l]==r[q+l];}void Da(){for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<n; i++) cnt[ x[i]=a[i] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=n-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;int p=1;for (int j=1; p<n; j<<=1,m=p){p=0;for (int i=n-j; i<n; i++) y[ p++ ]=i;for (int i=0; i<n; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;for (int i=0; i<m; i++) cnt[i]=0;for (int i=0; i<n; i++) cnt[ x[ y[i] ] ]++;for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];for (int i=n-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];p=1;swap(x,y);x[ sa[0] ]=0;for (int i=1; i<n; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;}}void Calc_height(){height[0]=0;int k=0;for (int i=0; i<n; i++){if (k) k--;if (!x[i]) continue;int j=sa[ x[i]-1 ];while (a[i+k]==a[j+k]) k++;height[ x[i] ]=k;}}bool Judge(int len){for (int i=0; i<n; i++) h[i]=t[i]=false;int last=0;for (int i=1; i<n; i++){if (height[i]<len){for (int j=last+1; j<i; j++){int nid=id[ sa[j-1] ];if (nid==1) h[ sa[j-1] ]=false;if (nid==2) t[ n-2-sa[j-1] ]=false;nid=id[ sa[j] ];if (nid==1) h[ sa[j] ]=false;if (nid==2) t[ n-2-sa[j] ]=false;}last=i;}else{int nid=id[ sa[i-1] ];if (nid==1){h[ sa[i-1] ]=true;if (t[ sa[i-1]+len-1 ]) return true;}if (nid==2){t[ n-2-sa[i-1] ]=true;if ( n-sa[i-1]-len-1>=0 &&  h[ n-sa[i-1]-len-1 ] ) return true;}nid=id[ sa[i] ];if (nid==1){h[ sa[i] ]=true;if (t[ sa[i]+len-1 ]) return true;}if (nid==2){t[ n-2-sa[i] ]=true;if ( n-sa[i]-len-1>=0 &&  h[ n-sa[i]-len-1 ] ) return true;}}}return false;}int Work(int len){for (int i=0; i<n; i++) h[i]=t[i]=false;int last=0,head=n;for (int i=1; i<n; i++){if (height[i]<len){for (int j=last+1; j<i; j++){int nid=id[ sa[j-1] ];if (nid==1) h[ sa[j-1] ]=false;if (nid==2) t[ n-2-sa[j-1] ]=false;nid=id[ sa[j] ];if (nid==1) h[ sa[j] ]=false;if (nid==2) t[ n-2-sa[j] ]=false;}last=i;}else{int nid=id[ sa[i-1] ];if (nid==1){h[ sa[i-1] ]=true;if (t[ sa[i-1]+len-1 ]) head=min(head,sa[i-1]);}if (nid==2){t[ n-2-sa[i-1] ]=true;if ( n-sa[i-1]-len-1>=0 &&  h[ n-sa[i-1]-len-1 ] )head=min(head,n-sa[i-1]-len-1);}nid=id[ sa[i] ];if (nid==1){h[ sa[i] ]=true;if (t[ sa[i]+len-1 ]) head=min(head,sa[i]);}if (nid==2){t[ n-2-sa[i] ]=true;if ( n-sa[i]-len-1>=0 &&  h[ n-sa[i]-len-1 ] )head=min(head,n-sa[i]-len-1);}}}return head;}void Binary(){int ans=1;int L=0,R=600;while (L+1<R){int mid=(L+R)>>1;if ( Judge(mid*2) ) L=mid;else R=mid;}ans=max(ans,L*2);L=0,R=600;while (L+1<R){int mid=(L+R)>>1;if ( Judge(mid*2+1) ) L=mid;else R=mid;}ans=max(ans,L*2+1);int head=Work(ans);for (int i=0; i<ans; i++) printf("%c",s[head+i]);printf("\n");}int main(){freopen("c.in","r",stdin);freopen("c.out","w",stdout);scanf("%s",&s);n=strlen(s);for (int i=0; i<n; i++){a[i]=(int)s[i]+1;id[i]=1;}for (int i=n+1; i<=2*n; i++){a[i]=a[2*n-i];id[i]=2;}a[n]=499;id[n]=3;n=n*2+2;a[n-1]=0;id[n-1]=4;m=500;Da();Calc_height();Binary();return 0;}

0 0
原创粉丝点击