[CC Snack Down Final]STRSUM

Description

有n个非空字符串s1~sn,现将其两两连接在一起得到了n*(n-1)个字符串L1 ~Ln*(n-1)
给你L1~Ln*(n-1),要你还原s1 ~ sn,有多解随便造一组即可
n<=50,∑|Li|<=10000

Solution

考虑将所有的Li和Si按长度为第一关键字,字典序为第二关键字排序
设S1<=S2<=S3,那么可以发现L1=Sx+S1或S1+Sx
那么我们可以枚举这个划分点,因为S1是最小的,就找到了S1
接下来考虑依次构造Si,假设我们已经找到了S1 ~ Si-1,将这些字符串构造出的串从L中删掉
考虑当前L中排名最前的串,它一定可以表示成Si+S1或S1+Si
但是如果S1既是L的前缀又是L的后缀怎么办
首先,如果S是合法串,那么S+S1和S1+S一定都出现过,我们判断是否合法
如果都合法我们找另一个串字典序最小的那个

Code

#include <set>
#include <vector>
#include <string>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

struct Str{
	string str;
	Str(string _st="") {str=_st;}
	friend bool operator < (Str a,Str b) {
		if (a.str.length()==b.str.length()) return a.str<b.str;
		return a.str.length()<b.str.length();
	}
};

multiset<Str> t;

typedef multiset<Str> :: iterator it;

int main() {
	int n;scanf("%d",&n);
	vector<Str> s(n*(n-1));
	fo(i,0,n*(n-1)-1) cin>>s[i].str;
	sort(s.begin(),s.end());
	fo(len,1,s[0].str.length()-1) {
		bool ok=1;t.clear();
		for(int j=1;j<n*(n-1);j++) t.insert(Str(s[j]));

		string a=s[0].str.substr(0,len);
		string b=s[0].str.substr(len);

		it pos=t.find(Str(b+a));
		if (pos==t.end()) continue;
		else t.erase(pos);

		vector<string> vec({a,b});

		if (b.length()<a.length()) swap(a,b);

		for(int j=1;j<n*(n-1);j++) {
			if (!t.count(Str(s[j]))) continue;
			string a1=s[j].str.substr(0,a.length());
			string c1=s[j].str.substr(a.length());

			string a2=s[j].str.substr(s[j].str.length()-a.length());
			string c2=s[j].str.substr(0,s[j].str.length()-a.length());

			if (a!=a1&&a!=a2) {ok=0;break;}

			string tmp;

			if (a==a1&&a==a2) {
				it p1,p2;
				p1=t.find(c1+a);p2=t.find(a+c2);
				if (p1==t.end()&&p2==t.end()) ok=0;
				else if (p1!=t.end()&&p2!=t.end()) tmp=(c1+a)<(a+c2)?c1:c2;
				else tmp=p1==t.end()?c2:c1;
			} else tmp=(a==a1)?c1:c2;
			
			if (!ok) break;

			for(auto st:vec) {
				it pos=t.find(st+tmp);
				if (pos==t.end()) {ok=0;break;}
				else t.erase(pos);
				pos=t.find(tmp+st);
				if (pos==t.end()) {ok=0;break;}
				else t.erase(pos);
			}
			if (!ok) break;
			vec.push_back(tmp);
		}
		if (ok&&vec.size()==n) {
			puts("Yes");
			for(int i=0;i<vec.size();i++) cout<<vec[i]<<endl;
			return 0;
		}
	}
	puts("No");
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值