题意
若有 n n n 个字符串 S i S_i Si,将它们两两拼接起来可以得到 n ( n − 1 ) n(n-1) n(n−1) 个字符串 L i L_i Li。已知 L L L,构造一个合法的 S S S,或判断无解。SPJ。 n ≤ 50 n\leq 50 n≤50, ∑ ∣ L i ∣ ≤ 5 × 1 0 4 \sum |L_i|\leq 5\times 10^4 ∑∣Li∣≤5×104。
题解
现将 S S S 和 L L L 都按长度排序,长度相同按字典序排序。
显然此时的 L 1 L_1 L1 就是 S 1 + S 2 S_1+S_2 S1+S2 或 S 2 + S 2 S_2+S_2 S2+S2。我们枚举在何处断开 L 1 L_1 L1,得到 S 1 S_1 S1 与 S 2 S_2 S2,并从 L L L 中删除 S 1 + S 2 S_1+S_2 S1+S2 和 S 2 + S 1 S_2+S_1 S2+S1。
S 3 S_3 S3 到 S n S_n Sn 的求法类似:当前 L L L 中的最小值肯定是 S 1 + S i S_1+S_i S1+Si 或 S i + S 1 S_i+S_1 Si+S1。我们判断 min L \min L minL 是否有前缀/后缀等于 S 1 S_1 S1,并将另一段作为 S i S_i Si,然后删除 S j + S i 和 S i + S j ( j < i ) S_j+S_i\text{ 和 }S_i+S_j(j<i) Sj+Si 和 Si+Sj(j<i)。假如前、后缀都符合要求,取较大的一个作为 S i S_i Si。如果这其中某一步找不到 S i S_i Si,则重新枚举断开 L 1 L_1 L1 的位置。
(记 ∑ ∣ L i ∣ = l \sum |L_i|=l ∑∣Li∣=l)字符串之间的比较可以用 hash 或 SA。因为 ∣ L 1 ∣ ≤ l n ( n − 1 ) |L_1|\leq \dfrac{l}{n(n-1)} ∣L1∣≤n(n−1)l,所以总复杂度分别为 O ( l log 2 l ) O(l\log ^2l) O(llog2l) 或 l log l l\log l llogl。
代码:
#include<bits/stdc++.h>
using namespace std;
int getint(){
int ans=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
ans=ans*10+c-'0';
c=getchar();
}
return ans;
}
const int LEN=5e4+10,N=54,M=N*N,L=17;
int l;
int n,m;
char s[LEN];
int sa[LEN],rk[LEN],tp[LEN],h[LEN];
int c[LEN];
void rsort(int m){
for(int i=1;i<=m;i++)c[i]=0;
for(int i=1;i<=l;i++)c[rk[tp[i]]]++;
for(int i=1;i<=m;i++)c[i]+=c[i-1];
for(int i=l;i>=1;i--)sa[c[rk[tp[i]]]--]=tp[i];
}
bool cmp(int x,int y,int k){
return tp[x]==tp[y]&&tp[x+k]==tp[y+k];
}
void get_sa(){
for(int i=1;i<=l;i++)rk[i]=s[i],tp[i]=i;
int m='z';
rsort(m);
for(int p=0,k=1;p<l;m=p,k<<=1){
p=0;
for(int i=l-k+1;i<=l;i++)tp[++p]=i;
for(int i=1;i<=l;i++)
if(sa[i]>k)tp[++p]=sa[i]-k;
rsort(m);
memcpy(tp,rk,sizeof(tp));
rk[sa[1]]=p=1;
for(int i=2;i<=l;i++)
rk[sa[i]]=cmp(sa[i],sa[i-1],k)?p:++p;
}
//for(int i=1;i<=l;i++)cerr<<sa[i]<<" ";cerr<<endl;
int t=0;
for(int i=1;i<=l;i++){
if(t)--t;
while(s[sa[rk[i]-1]+t]==s[i+t])++t;
h[rk[i]]=t;
}
}
int st[L][LEN],l2[LEN];
void init_st(){
l2[0]=-1;for(int i=1;i<=l;i++)st[0][i]=h[i+1],l2[i]=l2[i>>1]+1;
for(int i=1;i<L;i++){
for(int j=1;j<=l-(1<<i-1);j++){
st[i][j]=min(st[i-1][j],st[i-1][j+(1<<i-1)]);
}
}
}
int get_min(int l,int r){
if(l>r)swap(l,r);
if(l==r)return ::l-sa[l]+1;
int t=l2[r-l];
return min(st[t][l],st[t][r-(1<<t)]);
}
struct mstr{
int l,r;//[l,r)
mstr(){}
mstr(int l,int r):l(l),r(r){}
int len() const { return r-l; }
char operator[] (int x) const { return s[l+x]; }
mstr substr(int beg,int end) const { return mstr(l+beg,l+end); }
};
ostream& operator<< (ostream &out,const mstr& m){
for(int i=0;i<m.len();i++)out<<m[i];
return out;
}
pair<mstr,mstr> operator+ (const mstr &a,const mstr &b){ return make_pair(a,b); }
bool operator< (const mstr &a,const mstr &b){
//字典序比较字符串
int l=get_min(rk[a.l],rk[b.l]);
if(l>=a.len()||l>=b.len())return a.len()<b.len();
return a[l]<b[l];
}
bool operator== (const mstr &a,const mstr &b){
//比较字符串
if(a.len()!=b.len())return 0;
int l=get_min(rk[a.l],rk[b.l]);
return l>=a.len();
}
bool operator< (const mstr &a,const pair<mstr,mstr> &b){
//优先按长度比较 a 与 b.first+b.second
if(a.len()!=b.first.len()+b.second.len())
return a.len()<b.first.len()+b.second.len();
if(!(a.substr(0,b.first.len())==b.first))
return a.substr(0,b.first.len())<b.first;
return a.substr(b.first.len(),a.len())<b.second;
}
bool operator== (const mstr &a,const pair<mstr,mstr> &b){
//比较 a 与 b.first+b.second
if(a.len()!=b.first.len()+b.second.len())return 0;
return a.substr(0,b.first.len())==b.first&&a.substr(b.first.len(),a.len())==b.second;
}
struct Mstr_cmp{
bool operator()(const mstr &a,const mstr &b){
//优先按长度比较字符串
if(a.len()!=b.len())return a.len()<b.len();
return a<b;
}
}mstr_cmp;
mstr sl[M];
multiset<mstr,Mstr_cmp>ms;
int find(mstr a,mstr b){
//找到并删除 a+b
auto p=lower_bound(ms.begin(),ms.end(),a+b);
if(!(*p==a+b))return 0;
ms.erase(p);
return 1;
}
int main(){
n=getint(),m=n*(n-1);
int tmp=1;
for(int i=0;i<m;i++){
scanf("%s",s+tmp);
int l=strlen(s+tmp);
sl[i].l=tmp,sl[i].r=tmp+l;
tmp+=l;
}
l=tmp-1;
get_sa();
init_st();
for(int i=1;i<sl[0].len();i++){
{multiset<mstr,Mstr_cmp>qaq;swap(qaq,ms);}
vector<mstr>ans;
for(int i=0;i<m;i++)ms.insert(sl[i]);
mstr s1=ms.begin()->substr(0,i);
mstr s2=ms.begin()->substr(i,ms.begin()->len());
if(!(find(s1,s2)&&find(s2,s1)))continue;
ans.push_back(s1);ans.push_back(s2);
if(mstr_cmp(s2,s1))swap(s1,s2);
int l1=s1.len();
for(int j=3;j<=n;j++){
mstr p=*ms.begin();int lp=p.len();
mstr a1=p.substr(0,l1),a2=p.substr(l1,lp);
mstr b1=p.substr(0,lp-l1),b2=p.substr(lp-l1,lp);
if((!(a1==s1))&&!(b2==s1))goto gg;
mstr sx=(a1==s1&&b2==s1?max(a2,b1):(a1==s1?a2:b1));
for(int k=0;k<ans.size();k++){
if(!(find(ans[k],sx)&&find(sx,ans[k])))goto gg;
}
ans.push_back(sx);
}
puts("Yes");
for(auto j:ans){
for(int k=j.l;k<j.r;k++)putchar(s[k]);
putchar('\n');
}
return 0;
gg:;
}
puts("No");
return 0;
}