题目:
Problem F. Mr. Panda and Fantastic Beasts
题意:
有 n 个字符串,求第一个字符串的最短子串,满足这个子串在其余 n-1 个字符串中都没有出现过。
思路:
因为所有串的长度和小于等于250000,所以可以把这 n 个串合成一个串,为避免产生本不属于这些串的子串,每个串后都要加一个结束符,且把第一个串放在最后(这样就可以求后缀获得)。
例:
3
aba
cd
ab
合并后变成:cd*ab*aba 。
然后,求第一个串的所有后缀(也就是合并后串的后缀:aba、ba、a)与合并后串除这些后缀之外的其他后缀的最长公共前缀,所得的最长公共前缀再加之后的一个字母就是答案。求所有答案里最短的,长度一样求字典序最小的。
Code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 3e5+10;
const ll mod = 1e9+7;
/*
*倍增算法nlogn
*将待排序数组放在0~n-1中,在最后补一个0
*build(,n+1,);//注意是n+1
*getHeight(,n);
*例如:
*n = 8;
*num[] = { 1, 1, 2, 1, 1, 1, 1, 2, $ };注意num最后一位为0,其他大于0
*Rank[] = { 4, 6, 8, 1, 2, 3, 5, 7, 0 };Rank[0~n-1]为有效值,Rank[n]必定为0无效值
*sa[] = { 8, 3, 4, 5, 0, 6, 1, 7, 2 };sa[1~n]为有效值,sa[0]必定为n是无效值
*height[]= { 0, 0, 3, 2, 3, 1, 2, 0, 1 };height[2~n]为有效值
*/
string arr,tmp;
char fin[MAX];
char str[MAX];
int s[MAX];
bool vis[MAX];
int t1[MAX],t2[MAX],c[MAX],sa[MAX],rk[MAX],height[MAX];
void init()
{
memset(vis,0,sizeof(vis));
memset(t1,0,sizeof(t1));
memset(t2,0,sizeof(t2));
memset(c,0,sizeof(c));
memset(sa,0,sizeof(sa));
memset(rk,0,sizeof(rk));
memset(height,0,sizeof(height));
}
//求SA数组
void get_SA(int s[],int n,int m){
int *x=t1,*y=t2;
for(int i=0;i<m;i++) c[i]=0;
for(int i=0;i<n;i++) c[x[i]=s[i]]++;
for(int i=1;i<m;i++) c[i]+=c[i-1];
for(int i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
for(int k=1;k<=n;k<<=1){
int p=0;
for(int i=n-k;i<n;i++) y[p++]=i;
for(int i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
for(int i=0;i<m;i++) c[i]=0;
for(int i=0;i<n;i++) c[x[y[i]]]++;
for(int i=0;i<m;i++) c[i]+=c[i-1];
for(int i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=1;
x[sa[0]]=0;
for(int i=1;i<n;i++)
x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k] ? p-1:p++;
if(p>=n) break;
m=p;
}
}
//求height和rank数组
void get_height(int s[],int n)
{
int k=0;
for(int i=0;i<=n;i++)
rk[sa[i]]=i;
for(int i=0;i<n;i++){
if(k) k--;
int j=sa[rk[i]-1];
while(s[i+k]==s[j+k]) k++;
height[rk[i]]=k;
}
}
string ans;
string tmpans1;
string tmpans2;
int main()
{
int T;
cin>>T;
int Case=1;
while(T--)
{
arr.clear();
int n;
cin>>n;
scanf("%s",fin);
for(int i=1;i<n;i++){
cin>>tmp;
tmp+='0'; //结束符
arr+=tmp;
}
int len=0;
for(int i=0;i<arr.size();i++){
if(arr[i]=='0') s[len++]=1;
else s[len++]=arr[i]-'a'+2;
}
int pos=len;
for(int i=0;i<strlen(fin);i++){
arr+=fin[i];
s[len++]=fin[i]-'a'+2;
}
s[len]=0;
init();
get_SA(s,len+1,40);
get_height(s,len);
//把第一个串的所有后缀标记
for(int i=pos;i<len;i++){
vis[rk[i]]=true;
}
ans="";
int cnt=1e9+7;
tmpans1="";
tmpans2="";
for(int i=pos;i<len;i++){
int now = rk[i];
int tmpmi=1e9+7;
int fg=0;
int fg1=0,fg2=0;
int tmpmi1=1e9+7;
int tmpmi2=1e9+7;
tmpans1="";
tmpans2="";
//从当前排名往左找最长公共前缀
if(now==1){
tmpmi1=-1;
}
for(int j=now;j>=2;j--){
tmpmi=min(tmpmi,height[j]);
if(!vis[j-1]){
tmpmi1=tmpmi;
if(sa[now]+tmpmi<arr.size()){
fg1=1;
tmpans1="";
tmpans1.assign(arr,sa[now],tmpmi1+1);
}
else fg=1;
break;
}
}
//从当前排名往右找最长公共前缀
if(now==len){
tmpmi2=-1;
}
tmpmi=1e9+7;
for(int j=now+1;j<=len;j++){
tmpmi=min(tmpmi,height[j]);
if(!vis[j]){
tmpmi2=tmpmi;
if(sa[now]+tmpmi<arr.size()){
fg2=1;
tmpans2="";
tmpans2.assign(arr,sa[now],tmpmi2+1);
}
else fg=1;
break;
}
}
//取两者中的最坏情况为这种情况下的最优可行解
//再与之前的最优解比较
if(fg==1) continue;
if(fg1==0&&fg2==0) continue;
else if(fg1!=0&&fg2==0){
if(tmpmi1+1<cnt||(tmpmi1+1==cnt&&tmpans1<ans)){
ans=tmpans1;
cnt=ans.size();
}
}
else if(fg1==0&&fg2!=0){
if(tmpmi2+1<cnt||(tmpmi2+1==cnt&&tmpans2<ans)){
ans=tmpans2;
cnt=ans.size();
}
}
else{
if(tmpmi1>tmpmi2){
if(tmpmi1+1<cnt||(tmpmi1+1==cnt&&tmpans1<ans)){
ans=tmpans1;
cnt=ans.size();
}
}
else{
if(tmpmi2+1<cnt||(tmpmi2+1==cnt&&tmpans2<ans)){
ans=tmpans2;
cnt=ans.size();
}
}
}
}
printf("Case #%d: ",Case++);
if(cnt==1e9+7) cout<<"Impossible"<<endl;
else cout<<ans<<endl;
}
return 0;
}