POJ-3294-Life Forms(后缀数组-不小于 k 个字符串中的最长子串)
题意:
给定 n 个字符串,求出现在不小于 k 个字符串中的最长子串。
分析:
将 n 个字符串连起来,中间用不相同的且没有出现在字符串中的字符隔开,求后缀数组。
然后二分答案,将后缀分成若干组,判断每组的后缀是否出现在不小于 k 个的原串中。
如果是大于127, char 是负数, 在计数排序的时候是会出问题的。
这题在输出上WA了很多次。最后下载了数据才找出来的。。。。
// File Name: 3294.cpp // Author: Zlbing // Created Time: 2013年09月07日 星期六 16时21分37秒 #include<iostream> #include<string> #include<algorithm> #include<cstdlib> #include<cstdio> #include<set> #include<map> #include<vector> #include<cstring> #include<stack> #include<cmath> #include<queue> using namespace std; #define CL(x,v); memset(x,v,sizeof(x)); #define INF 0x3f3f3f3f #define LL long long #define REP(i,r,n) for(int i=r;i<=n;i++) #define RREP(i,n,r) for(int i=n;i>=r;i--) //rank从0开始 //sa从1开始,因为最后一个字符(最小的)排在第0位 //height从2开始,因为表示的是sa[i-1]和sa[i] const int MAXN=220000; int rank[MAXN],sa[MAXN],X[MAXN],Y[MAXN],height[MAXN]; int s[MAXN]; int buc[MAXN]; int T[MAXN]; void calheight(int n) { int i , j , k = 0; for(i = 1 ; i <= n ; i++) rank[sa[i]] = i; for(i = 0 ; i < n ; height[rank[i++]] = k) for(k?k--:0 , j = sa[rank[i]-1] ; s[i+k] == s[j+k] ; k++); } bool cmp(int *r,int a,int b,int l) { return (r[a] == r[b] && r[a+l] == r[b+l]); } void suffix(int n,int m = 128) { int i , l , p , *x = X , *y = Y; for(i = 0 ; i < m ; i ++) buc[i] = 0; for(i = 0 ; i < n ; i ++) buc[ x[i] = s[i] ] ++; for(i = 1 ; i < m ; i ++) buc[i] += buc[i-1]; for(i = n - 1; i >= 0 ; i --) sa[ --buc[ x[i] ]] = i; for(l = 1,p = 1 ; p < n ; m = p , l *= 2) { p = 0; for(i = n-l ; i < n ; i ++) y[p++] = i; for(i = 0 ; i < n ; i ++) if(sa[i] >= l) y[p++] = sa[i] - l; for(i = 0 ; i < m ; i ++) buc[i] = 0; for(i = 0 ; i < n ; i ++) buc[ x[y[i]] ] ++; for(i = 1 ; i < m ; i ++) buc[i] += buc[i-1]; for(i = n - 1; i >= 0 ; i --) sa[ --buc[ x[y[i]] ] ] = y[i]; for(swap(x,y) , x[sa[0]] = 0 , i = 1 , p = 1 ; i < n ; i ++) x[ sa[i] ] = cmp(y,sa[i-1],sa[i],l) ? p-1 : p++; } calheight(n-1);//后缀数组关键是求出height,所以求sa的时候顺便把rank和height求出来 } int vis[1005]; bool solve(int x,int k,int n) { CL(vis,0); vis[0]=1; int tot=0; if(!vis[T[sa[1]]]) tot++; vis[T[sa[1]]]=1; for(int i=2;i<=n;i++) { if(height[i]<x) { tot=0; CL(vis,0); vis[0]=1; if(!vis[T[sa[i]]]) tot++; vis[T[sa[i]]]=1; continue; } if(!vis[T[sa[i]]])tot++; if(tot>=k)return true; vis[T[sa[i]]]=1; } return false; } void print(int x,int k,int n) { CL(vis,0); int tot=0; vis[0]=1; if(!vis[T[sa[0]]]) tot++; vis[T[sa[0]]]=1; for(int i=1;i<=n;i++) { if(height[i]<x) { if(tot>=k) { for(int j=0;j<x;j++) printf("%c",s[sa[i-1]+j]-6); printf("\n"); } tot=0; CL(vis,0); vis[0]=1; if(!vis[T[sa[i]]]) tot++; vis[T[sa[i]]]=1; continue; } if(!vis[T[sa[i]]])tot++; vis[T[sa[i]]]=1; } //这里一开始没写导致WA了很多次 if(tot>=k) { for(int j=0;j<x;j++) printf("%c",s[sa[n]+j]-6); printf("\n"); } } int main() { //freopen("C.dat","r",stdin); //freopen("Cout.dat","w",stdout); int N; char ch[1105]; int first=0; while(~scanf("%d",&N)) { if(!N)break; if(first)printf("\n"); first++; int k=N/2+1; int n=0; int tt=1; int L=1,R=0; REP(i,1,N) { scanf("%s",ch); int len=strlen(ch); R=max(R,len); REP(j,0,len-1) { s[n++]=(int)ch[j]+6; T[n-1]=i; } s[n++]=tt++; T[n-1]=0; } //printf("case %d:",first); if(N==1) { printf("%s\n",ch); continue; } s[n-1]=0; T[n-1]=0; n--; suffix(n+1,200); int ans=-1; while(L<=R) { int mid=L+(R-L+1)/2; if(solve(mid,k,n)) { ans=max(ans,mid); L=mid+1; } else R=mid-1; } //printf("%d\n",ans); if(ans!=-1) print(ans,k,n); else printf("?\n"); } return 0; }