Mr. Panda and Fantastic Beasts(EC2016 字尾陣列)
阿新 • • 發佈:2018-11-25
題目:
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; }