在掌握POJ 2774(两个串求最长公共子串)以及对Height数组分组后,本题还是容易想出思路的。
首先用字符集外的不同字符连接所有串,这是为了防止两个后缀在比较时超过某个字符串的分界。二分子串的长度,扫描height数组,判定是否有某个分组来源与至少K个原字符串(本题要求出现超过n的一半次)。
#include <iostream> #include <vector> #include <algorithm> #include <string> #include <string.h> #include <stdio.h> #include <queue> #include <stack> #include <map> #include <set> #include <cmath> #include <ctime> #include <cassert> #include <sstream> using namespace std; const int N=2e6+110; int sa[N]; int t1[N],t2[N],c[N]; int rk[N],height[N]; inline int cmp(int *r,int a,int b,int l){ return r[a]==r[b]&&r[a+l]==r[b+l]; } char s[N]; void calcSA (char *s,int n,int m) { int i,j,p,*x=t1,*y=t2; for(i=0;i<m;i++)c[i]=0; for(i=0;i<n;i++)c[x[i]=s[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<<=1){ p=0; for(i=n-j;i<n;i++)y[p++]=i; for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j; // 排名从小到大,如果pos比j大,则suffix(sa[i]-j)的第二关键字为p for(i=0;i<m;i++)c[i]=0; for(i=0;i<n;i++)c[x[y[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]; // 根据第二关键字从大到小,确定新一轮sa swap(x,y); p=1;x[sa[0]]=0; for(i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; if(p>=n)break; m=p; } } void calcHeight(char *s,int n) { int i,j,k=0; for(i=0;i<=n;i++)rk[sa[i]]=i; for(i=0;i<n;i++){ if(k)k--; // h[i]>=h[i-1]-1 j=sa[rk[i]-1]; // suffix(j)排名在suffix(i)前一位 while(s[i+k]==s[j+k])k++; // 暴力计算lcp height[rk[i]]=k; } } int belong[N]; vector<int>ans; bool vis[128]; bool ok(int n,int m,int k) { memset(vis,0,sizeof vis); int cnt=1; vis[belong[sa[1]]]=true; vector<int>ret; bool push=false; for (int i=2;i<=n;i++) { if (height[i]<m) { memset(vis,0,sizeof vis); push=false; vis[belong[sa[i]]]=true; cnt=1; } else if (!push){ if (!vis[belong[sa[i]]]) { vis[belong[sa[i]]]=true; ++cnt; } if (cnt>k/2&&!push) { push=true; ret.push_back(sa[i]); } } } //cout<<"go "<<m<<" "<<ret.size()<<endl; if (ret.size()>0) { ans=ret; return true; } else return false; } int main () { int n; while (scanf("%d",&n)!=EOF,n) { int p=0; int maxLen=0; for (int i=1;i<=n;i++) { scanf("%s",s+p); int l=strlen(s+p); maxLen=max(maxLen,l); int np=p+l; for (int j=p;j<np;j++) { belong[j]=i; s[j]+=5; // 这里+5是为了保证插入的分隔符不在字符集中出现,n至多为100,a的ASCII为97 } belong[np]=111; p=np; s[p++]=i; } s[--p]=0; belong[p]=-1; calcSA(s,p+1,550); calcHeight(s,p); int l=0,r=maxLen,ret=0; while (l<=r) { int m=(l+r)>>1; if (ok(p,m,n)) { ret=m; l=m+1; } else r=m-1; } if (ret==0) { puts("? "); } else { for (int i=0;i<ans.size();i++) { int beg=ans[i]; for (int j=0;j<ret;j++) printf("%c",s[beg+j]-5); puts(""); } puts(""); } } return 0; }