题目大意:给n个字符串,求最大长度的字符串,使其在一半以上的字符串中出现过,按字典序输出
后缀数组。。。二分最大长度,再按sa数组的顺序记录不同最长字符串出现的位置。
代码:
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<set>
#include<vector>
using namespace std;
const int N=2e5;
char t[N];
int s[N],sa[N],r[N],h[N],c[N],x[N],y[N],be[N],len,vis[N];
set<int>se;
vector<int>g;
void build(int n,int m){
int i,j,k;
for(i=0;i<m;i++) c[i]=0;
for(i=0;i<n;i++){
x[i]=s[i];
c[x[i]]++;
}
for(i=1;i<m;i++) c[i]+=c[i-1];
for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
for(j=1;j<n;j*=2){
k=0;
for(i=n-j;i<n;i++) y[k++]=i;
for(i=0;i<n;i++) if(sa[i]>=j) y[k++]=sa[i]-j;
for(i=0;i<m;i++) c[i]=0;
for(i=0;i<n;i++) c[x[i]]++;
for(i=1;i<m;i++) c[i]+=c[i-1];
for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
swap(x,y);
m=0;
x[sa[0]]=m++;
for(i=1;i<n;i++){
if(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+j]==y[sa[i-1]+j]) x[sa[i]]=m-1;
else x[sa[i]]=m++;
}
if(m==n) break;
}
for(i=0;i<n;i++) r[sa[i]]=i;
k=0;
for(i=0;i<n-1;i++){
if(k) k--;
j=sa[r[i]-1];
while(s[i+k]==s[j+k]) k++;
h[r[i]]=k;
}
//for(i=0;i<n;i++) printf("%d\n",h[i]);
}
int f(int x,int k){
int i,l,r,sum=0,j;
l=r=-1;
memset(vis,0,sizeof(vis)); //vis数组判断在那个字符串中出现过
for(i=1;i<len;i++){
if(h[i]>=x){
if(h[i-1]<x) l=i;
r=i;
if(vis[be[sa[i]]]==0) sum++;
vis[be[sa[i]]]=1;
if(vis[be[sa[i-1]]]==0) sum++;
vis[be[sa[i-1]]]=1;
}
else{
if(sum>k) return 1;
if(r!=-1) for(j=l;j<=r;j++) vis[be[sa[j]]]=vis[be[sa[j-1]]]=0; //最长公共前缀小于二分的长度x,重新记录
l=r=-1;
sum=0;
}
}
return 0;
}
void find(int x,int k){
g.clear();
memset(vis,0,sizeof(vis));
int i,pos,j,l=-1,r=-1,sum=0;;
for(i=1;i<len;i++){
if(h[i]>=x){
if(h[i-1]<x) l=i;
r=i;
pos=sa[i];
if(vis[be[sa[i]]]==0) sum++;
vis[be[sa[i]]]=1;
if(vis[be[sa[i-1]]]==0) sum++;
vis[be[sa[i-1]]]=1;
}
else{
if(sum>k) g.push_back(pos);
if(r!=-1) for(j=l;j<=r;j++) vis[be[sa[j]]]=vis[be[sa[j-1]]]=0;
l=r=-1;
sum=0;
}
}
}
int main(){
int i,n,j,m;
while(scanf("%d",&n)!=EOF){
if(n==0) break;
if(n==1){
scanf("%s",t);
printf("%s\n\n",t);
continue;
}
len=0;
memset(be,0,sizeof(be));
for(i=1;i<=n;i++){
scanf("%s",t);
m=strlen(t);
for(j=0;j<m;j++){
be[len]=i;
s[len++]=t[j];
}
s[len++]=1000+i;
}
s[len-1]=0;
build(len,2000);
int l=0,r=len,mid;
while(l+1<r){
mid=(l+r)/2;
if(f(mid,n/2)) l=mid;
else r=mid;
}
if(l==0) printf("?\n\n");
else{
find(l,n/2);
for(i=0;i<g.size();i++){
for(j=g[i];j<g[i]+l;j++) printf("%c",s[j]);
printf("\n");
}
printf("\n");
}
}
return 0;
}