pku3450 Corporate Identity
思路:以第一个串为基串,将另外N-1个串分别与基串拼接跑后缀数组,记录基串每一个位置与另外一个串的最长匹配长度,求出每一个位置在和别的所有串跑后缀数组时所得的最长匹配中的最小值,最后求出所有位置中的最大值就是所求。
也可以用类似Music Theme的二分枚举搞掉,但是代码太丑陋所以就不贴啦
/*
Memory: 428K Time: 188MS
Language: C++ Result: Accepted
*/
#include <iostream>
using namespace std;
#define MAXN 500
int b[MAXN],array[4][MAXN],*sa,*nsa,*rank,*nrank,height[MAXN],len,n,m,mnlen[MAXN],mxlen[MAXN];
char str[4001][205],ss[MAXN];
void make_sa(){
int i,k;
sa=array[0];
nsa=array[1];
rank=array[2];
nrank=array[3];
memset(b,0,sizeof(b));
for(i=0;i<n;i++)
b[ss[i]]++;
for(i=1;i<=256;i++)
b[i]+=b[i-1];
for(i=n-1;i>=0;i--)
sa[--b[ss[i]]]=i;
for(rank[sa[0]]=0,i=1;i<n;i++){
rank[sa[i]]=rank[sa[i-1]];
if(ss[sa[i]]!=ss[sa[i-1]])
rank[sa[i]]++;
}
for(k=1;k<n && rank[sa[n-1]]<n-1;k*=2){
for(i=0;i<n;i++)
b[rank[sa[i]]]=i;
for(i=n-1;i>=0;i--)
if(sa[i]-k>=0)
nsa[b[rank[sa[i]-k]]--]=sa[i]-k;
for(i=n-k;i<n;i++)
nsa[b[rank[i]]--]=i;
for(nrank[nsa[0]]=0,i=1;i<n;i++){
nrank[nsa[i]]=nrank[nsa[i-1]];
if(rank[nsa[i]]!=rank[nsa[i-1]] || rank[nsa[i]+k]!=rank[nsa[i-1]+k])
nrank[nsa[i]]++;
}
int *t=sa;sa=nsa;nsa=t;
t=rank;rank=nrank;nrank=t;
}
}
void cal_height(){
int i,j,k;
for(i=0,k=0;i<n;i++){
if(rank[i]==0)
height[rank[i]]=0;
else{
for(j=sa[rank[i]-1];ss[i+k]==ss[j+k];k++);
height[rank[i]]=k;
if(k>0)
k--;
}
}
}
void make_mxlen(){
memset(mxlen,0,sizeof(mxlen));
int i,mn=INT_MAX;
mxlen[0]=0;
for(i=1;i<n;i++){
if(height[i]<mn)
mn=height[i];
if(sa[i]<len){
if(mn==INT_MAX)
mxlen[sa[i]]=0;
else
mxlen[sa[i]]=mn;
}
else{
mn=INT_MAX;
}
}
bool first=true;
mn=INT_MAX;
for(i=n-1;i>=1;i--){
if(sa[i]<len){
if(mn!=INT_MAX)
if(mn>mxlen[sa[i]])
mxlen[sa[i]]=mn;
}
else{
mn=height[i];
first=false;
}
if(!first && height[i]<mn)
mn=height[i];
}
}
int main(){
int i,j,t,mx,mxtag;
while(scanf("%d",&m) && m){
memset(mnlen,0x7f,sizeof(mnlen));
for(i=0;i<m;i++)
scanf("%s",str[i]);
len=strlen(str[0]);
str[0][len++]='#';
str[0][len]='\0';
for(i=1;i<m;i++){
strcpy(ss,str[0]);
strcat(ss,str[i]);
n=len+strlen(str[i]);
ss[n++]='$';
ss[n]='\0';
make_sa();
cal_height();
//记录基串每一个位置与另外一个串的最长匹配长度
make_mxlen();
//求出每一个位置在和别的所有串跑后缀数组时所得的最长匹配中的最小值
for(j=0;j<len;j++)
if(mxlen[j]<mnlen[j])
mnlen[j]=mxlen[j];
}
strcpy(ss,str[0]);
n=len;
//若有多个匹配符合答案则输出字典序最小的
//估需要对基串重建后缀数组
make_sa();
mx=0;
for(i=1;i<n;i++){
if(mnlen[sa[i]]>mx){
mx=mnlen[sa[i]];
mxtag=sa[i];
}
}
if(mx){
for(i=0;i<mx;i++)
printf("%c",ss[mxtag+i]);
printf("\n");
}
else
printf("IDENTITY LOST\n");
}
return 0;
}
Memory: 428K Time: 188MS
Language: C++ Result: Accepted
*/
#include <iostream>
using namespace std;
#define MAXN 500
int b[MAXN],array[4][MAXN],*sa,*nsa,*rank,*nrank,height[MAXN],len,n,m,mnlen[MAXN],mxlen[MAXN];
char str[4001][205],ss[MAXN];
void make_sa(){
int i,k;
sa=array[0];
nsa=array[1];
rank=array[2];
nrank=array[3];
memset(b,0,sizeof(b));
for(i=0;i<n;i++)
b[ss[i]]++;
for(i=1;i<=256;i++)
b[i]+=b[i-1];
for(i=n-1;i>=0;i--)
sa[--b[ss[i]]]=i;
for(rank[sa[0]]=0,i=1;i<n;i++){
rank[sa[i]]=rank[sa[i-1]];
if(ss[sa[i]]!=ss[sa[i-1]])
rank[sa[i]]++;
}
for(k=1;k<n && rank[sa[n-1]]<n-1;k*=2){
for(i=0;i<n;i++)
b[rank[sa[i]]]=i;
for(i=n-1;i>=0;i--)
if(sa[i]-k>=0)
nsa[b[rank[sa[i]-k]]--]=sa[i]-k;
for(i=n-k;i<n;i++)
nsa[b[rank[i]]--]=i;
for(nrank[nsa[0]]=0,i=1;i<n;i++){
nrank[nsa[i]]=nrank[nsa[i-1]];
if(rank[nsa[i]]!=rank[nsa[i-1]] || rank[nsa[i]+k]!=rank[nsa[i-1]+k])
nrank[nsa[i]]++;
}
int *t=sa;sa=nsa;nsa=t;
t=rank;rank=nrank;nrank=t;
}
}
void cal_height(){
int i,j,k;
for(i=0,k=0;i<n;i++){
if(rank[i]==0)
height[rank[i]]=0;
else{
for(j=sa[rank[i]-1];ss[i+k]==ss[j+k];k++);
height[rank[i]]=k;
if(k>0)
k--;
}
}
}
void make_mxlen(){
memset(mxlen,0,sizeof(mxlen));
int i,mn=INT_MAX;
mxlen[0]=0;
for(i=1;i<n;i++){
if(height[i]<mn)
mn=height[i];
if(sa[i]<len){
if(mn==INT_MAX)
mxlen[sa[i]]=0;
else
mxlen[sa[i]]=mn;
}
else{
mn=INT_MAX;
}
}
bool first=true;
mn=INT_MAX;
for(i=n-1;i>=1;i--){
if(sa[i]<len){
if(mn!=INT_MAX)
if(mn>mxlen[sa[i]])
mxlen[sa[i]]=mn;
}
else{
mn=height[i];
first=false;
}
if(!first && height[i]<mn)
mn=height[i];
}
}
int main(){
int i,j,t,mx,mxtag;
while(scanf("%d",&m) && m){
memset(mnlen,0x7f,sizeof(mnlen));
for(i=0;i<m;i++)
scanf("%s",str[i]);
len=strlen(str[0]);
str[0][len++]='#';
str[0][len]='\0';
for(i=1;i<m;i++){
strcpy(ss,str[0]);
strcat(ss,str[i]);
n=len+strlen(str[i]);
ss[n++]='$';
ss[n]='\0';
make_sa();
cal_height();
//记录基串每一个位置与另外一个串的最长匹配长度
make_mxlen();
//求出每一个位置在和别的所有串跑后缀数组时所得的最长匹配中的最小值
for(j=0;j<len;j++)
if(mxlen[j]<mnlen[j])
mnlen[j]=mxlen[j];
}
strcpy(ss,str[0]);
n=len;
//若有多个匹配符合答案则输出字典序最小的
//估需要对基串重建后缀数组
make_sa();
mx=0;
for(i=1;i<n;i++){
if(mnlen[sa[i]]>mx){
mx=mnlen[sa[i]];
mxtag=sa[i];
}
}
if(mx){
for(i=0;i<mx;i++)
printf("%c",ss[mxtag+i]);
printf("\n");
}
else
printf("IDENTITY LOST\n");
}
return 0;
}