题意:输入n个串,求一个长度最大的串,使得它在超过一半的串中连续出现。如果有多解,按字典序从小到大输出。
思路:后缀数组。首先把所有原串拼接成一个长串,中间插入不同的没出现过的字符。然后对长串求后缀数组的height数组,二分答案,检查是否有该长度的串在超过一半的串中连续出现。方法是分段,每次发现height[i]<ans就划为一个新段,则每段前ans个字符全部相同。开一个数组来标记包含了哪些串。
这是大白书后缀数组的例题。SA真是神一样的数据结构啊,太难看懂了,直到我发现了一个比较好理解的板子才看懂的。题目为什么能这样做呢,我们可以想象一颗后缀树,把拼接后的长串的所有后缀插入到一棵后缀树中(顺便领悟一下插入不同没出现过的字符的精妙)。前ans个字符相同的后缀,一定共用了ans个节点,如果哪个节点下面的分支非常多,那么就说明有多个这样的串了。height数组相当与保存了两个相邻叶子公共祖先的数量。
写这题的时候我出现了三处错误,一是二分没有写好,有的值分不到;二是插入的字符从‘z’开始往后插,但是这样做会出现bug,可能是溢出了;三是分段时最后一段没分好。
#include <iostream>
#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <iomanip>
#include <cstdlib>
#include <string>
#include <string.h>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <ctype.h>
#include <sstream>
#define INF 1000000000
#define ll long long
#define min3(a,b,c) min(a,min(b,c))
#define max3(a,b,c) max(a,max(b,c))
using namespace std;
const int N=110000;
struct SuffixArray{
int n,len,A[N],C[N],sa[N],rank[N],height[N];
struct RadixEle{
int id,k[2];
RadixEle(){}
RadixEle(int a,int b,int c){id=a;k[0]=b;k[1]=c;}
}RT[N],RE[N];
void RadixSort()
{
for(int y=1;y>=0;y--)
{
memset(C,0,sizeof(C));
for(int i=1;i<=n;i++) C[RE[i].k[y]]++;
for(int i=1;i<N;i++) C[i]+=C[i-1];
for(int i=n;i>=1;i--) RT[C[RE[i].k[y]]--]=RE[i];
for(int i=1;i<=n;i++) RE[i]=RT[i];
}
for(int i=1;i<=n;i++)
{
rank[RE[i].id]=rank[RE[i-1].id];
if(RE[i].k[0]!=RE[i-1].k[0]||RE[i].k[1]!=RE[i-1].k[1])
rank[RE[i].id]++;
}
}
void CalcSA(){
for(int i=1;i<=n;i++) RE[i]=RadixEle(i,A[i],0);
RadixSort();
for(int k=1;1+k<=n;k*=2)
{
for(int i=1;i<=n;i++)
RE[i]=RadixEle(i,rank[i],(i+k<=n?rank[i+k]:0));
RadixSort();
}
for(int i=1;i<=n;i++) sa[rank[i]]=i;
}
void CalcHeight()
{
int h=0;
for(int i=1;i<=n;i++)
{
if(rank[i]==1) h=0;
else
{
int k=sa[rank[i]-1];
if(--h<0) h=0;
while(A[i+h]==A[k+h]) h++;
}
height[rank[i]]=h;
}
}
}SA;
char str[110000];
int mp[110000];
bool vis[110];
int n;
bool binaryans(int len){
int s=1;
int t=0;
for(int j=1;j<=n;j++)vis[j]=0;
for(int i=1;i<=SA.n;i++){
vis[mp[SA.sa[i]]]=1;
if(SA.height[i+1]<len||i==SA.n){
int cnt=0;
for(int j=1;j<=n;j++)if(vis[j])cnt++;
if(cnt>n/2)return 1;
for(int j=1;j<=n;j++)vis[j]=0;
s=i;
}
t++;
}
return 0;
}
void printans(int len){
for(int j=1;j<=n;j++)vis[j]=0;
for(int i=1;i<=SA.n+1;i++){
vis[mp[SA.sa[i]]]=1;
if(SA.height[i+1]<len||i==SA.n){
int cnt=0;
for(int j=1;j<=n;j++)if(vis[j])cnt++;
if(cnt>n/2){
int s=SA.sa[i-1];
for(int k=s;k<s+len;k++){
printf("%c",str[k]);
}
printf("\n");
cnt=0;
}
for(int j=1;j<=n;j++)vis[j]=0;
}
}
}
int main(){
while(cin>>n){
if(!n)break;
char* s=str+1;
int ins=1;
for(int i=1;i<=n;i++){
scanf("%s",s);
int t=strlen(s);
s[t]=ins++;
if(ins=='a')ins='z'+1;
s+=t+1;
}
int siz=strlen(str+1);
int k=1;
for(int i=1;i<=siz;i++){
SA.A[i]=str[i];
mp[i]=k;
if(SA.A[i]<'a'||SA.A[i]>'z')k++;
}
SA.n=siz; SA.CalcSA();
SA.CalcHeight();
int l=0,r=siz/n;
int ans=0;
while(l<r){
int mid=(l+r)/2;
if(binaryans(mid)){
ans=mid;
l=mid+1;
}else{
r=mid;
}
}
if(ans){
printans(ans);
}else{
printf("?\n");
}
printf("\n");
}
return 0;
}