引用罗穗骞论文中的话:
将n 个字符串连起来,中间用不相同的且没有出现在字符串中的字符隔开,求后缀数组。然后二分答案,用和例3 同样的方法将后缀分成若干组,判断每组的后缀是否出现在不小于k 个的原串中。这个做法的时间复杂度为O(nlogn)。
#include<cstdio> #include<iostream> #include<algorithm> #include<vector> #include<cstring> #include<cstdlib> using namespace std; const int N = 110008; int wa[N],wb[N],wv[N],ws0[N]; int val[N], sum[N], tp1[N], tp2[N]; int sa[N], rank[N], height[N]; int hs[N]; char tp[1008]; int str[N]; bool vis[1000]; vector<int> v; int cmp(int *r,int a,int b,int l) {return r[a]==r[b]&&r[a+l]==r[b+l];} void da(int *r,int *sa,int n,int m) { int i,j,p,*x=wa,*y=wb,*t; for(i=0;i<m;i++) ws0[i]=0; for(i=0;i<n;i++) ws0[x[i]=r[i]]++; for(i=1;i<m;i++) ws0[i]+=ws0[i-1]; for(i=n-1;i>=0;i--) sa[--ws0[x[i]]]=i; for(j=1,p=1;p<n;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<n;i++) wv[i]=x[y[i]]; for(i=0;i<m;i++) ws0[i]=0; for(i=0;i<n;i++) ws0[wv[i]]++; for(i=1;i<m;i++) ws0[i]+=ws0[i-1]; for(i=n-1;i>=0;i--) sa[--ws0[wv[i]]]=y[i]; for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; } return; } void calheight(int *r,int *sa,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];r[i+k]==r[j+k];k++); return; } bool check(int m, int n, int num){ memset(vis , 0, sizeof(vis)); int cnt = 0; int size = 0; for(int i = 1; i <= n ;i++){ if(height[i] >= m){//分组,同组内计数 if(!vis[hs[ sa[i] ]]){ cnt++; vis[hs[ sa[i] ]] = 1; } if(!vis[hs[ sa[i - 1] ]]){//不同组 cnt++; vis[hs[ sa[i - 1] ]] = 1; } }else{ if(cnt > num){ if(size == 0){ v.clear(); } size++; v.push_back(sa[i - 1]); } cnt = 0; memset(vis, 0 , sizeof(vis)); } } if(size){ return true; }else{ return false; } } int main(){ int k; bool flag = 0; while(~scanf("%d", &k ) && k){ v.clear(); int n = 0; int mini = 100000000; for(int t = 0; t < k; t++){ scanf("%s", tp); int len = strlen(tp); mini = min(mini, len); for(int i = n, j = 0; j < len; i++, j++){ str[i] = tp[j]; hs[i] = t; } n += len + 1; if(t != k - 1){ str[n - 1] = t + 130; } } n--; str[n] = 0; da(str, sa , n + 1 , 256); calheight(str, sa, n); int l = 1, r = mini; int ans = 0; while(l <= r){ int m = (l + r)>>1; if(check(m, n, k / 2 )){ l = m + 1; ans = m; }else{ r = m - 1; } } if(flag){ printf(" "); } flag = 1; if(ans){ for(int i = 0; i < v.size(); i++){ int st = v[i]; for(int j = 0; j < ans; j++){ printf("%c", str[st + j]); } printf(" "); } }else{ printf("? "); } } return 0; }