某 SCOI 模拟赛 T1 黑箱(blackbox)【字符串】

题意

若有 n n n 个字符串 S i S_i Si,将它们两两拼接起来可以得到 n ( n − 1 ) n(n-1) n(n1) 个字符串 L i L_i Li。已知 L L L,构造一个合法的 S S S,或判断无解。SPJ。 n ≤ 50 n\leq 50 n50 ∑ ∣ L i ∣ ≤ 5 × 1 0 4 \sum |L_i|\leq 5\times 10^4 Li5×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)} L1n(n1)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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值