题解:
后缀数组
把所有的字符串用不同的分隔符连接起来
然后求height,最后二分求长度为mid的子串是否有满足条件的
记录满足条件的所有子串的首位置
代码:
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> typedef long long LL; using namespace std; const int N=200000+10; int *rank,r[N],sa[N],height[N],size,wa[N],wb[N],wm[N],len[110],pos[N],mark[110]; char s[N]; int cmp(int *r,int a,int b,int l) { return r[a]==r[b]&&r[a+l]==r[b+l]; } void makesa(int *r,int *sa,int n,int m){ int *x=wa,*y=wb; for (int i=0;i<m;i++)wm[i]=0; for (int i=0;i<n;i++)wm[x[i]=r[i]]++; for (int i=1;i<m;i++)wm[i]+=wm[i-1]; for (int i=n-1;i>=0;i--)sa[--wm[x[i]]]=i; for (int i=0,j=1,p=0;p<n;j=j*2,m=p) { for (p=0,i=n-j;i<n;i++)y[p++]=i; for (i=0;i<n;i++) if (sa[i]>=j)y[p++]=sa[i]-j; for (i=0;i<m;i++)wm[i]=0; for (i=0;i<n;i++)wm[x[y[i]]]++; for (i=1;i<m;i++)wm[i]+=wm[i-1]; for (i=n-1;i>=0;i--)sa[--wm[x[y[i]]]]=y[i]; for (t=x,x=y,y=t,i=p=1,x[sa[0]]=0;i<n;i++) x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++; } rank=x; } void calheight(int *r,int *sa,int n) { for (int i=0,j=0,k=0;i<n;height[rank[i++]]=k) for (k?--k:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++); } int check(int mid,int n,int k) { memset(mark,false,sizeof mark); int num=0,ans=0; for (int i=1;i<=n;i++) { if (height[i]>=mid) { for (int j=1;j<=size;j++) { if (sa[i]>len[j-1]&&sa[i]<len[j])ans+=(mark[j]?0:1),mark[j]=1; if (sa[i-1]>len[j-1]&&sa[i-1]<len[j])ans+=(mark[j]?0:1),mark[j]=1; } } else { if (ans>k/2)pos[++num]=sa[i-1]; ans=0; memset(mark,false,sizeof mark); } } if (ans>k/2)pos[++num]=sa[n]; if (num){pos[0]=num;return 1;} return 0; } int main() { int k,n,num=0; len[0]=-1; while(scanf("%d",&k),k) { pos[0]=size=n=0; for(int i=0;i<k;i++) { scanf("%s",s+n); for(;s[n]!='\0';n++)r[n]=s[n]; r[len[++size]=n++]=300+i; } r[n-1]=0; makesa(r,sa,n,400); calheight(r,sa,n-1); int L=1,R=n; while (L<=R) { int mid=L+R>>1; if(check(mid,n,k))L=mid+1; else R=mid-1; } if (num++)puts(""); if (L-1==0)puts("?"); else { for (int i=1;i<=pos[0];i++) { for (int j=pos[i];j<pos[i]+L-1;j++)printf("%c",s[j]); puts(""); } } } return 0; }