题意
DNA序列
dna.c/cpp/in/out
时间限制:2s
空间限制:2G
题目描述
2018年10月,MIT建立了最新的纳米科技研究中心MIT.nano。此后,不断有新的研究成果在此产生。
有一天,研究者发现了一种新的生物,这种生物的基因中含有 条DNA序列,每一条都有一定的长度,
科学家们可以将每条DNA序列切断,从而取出它的一个非空前缀。此后,他们可以将这些前缀按任意
顺序连结起来形成一条完整的DNA序列,这样的DNA序列对治疗癌症有很大的作用。
每条DNA序列都仅包含大写字母“A”,“C”,“G”,“T”。
科学家们很快发现,DNA序列的字典序越小,则治疗癌症的效果越好,他们想知道给定 条DNA序列,
他们能获得的字典序最小的DNA序列是什么。
输入格式
第一行一个正整数
n
n
n ,表示DNA序列的个数。
接下来
n
n
n行,每行一个非空字符串表示一条DNA,保证每个字符均为“A”,“C”, “G”,“T”中的一个。
输出格式
输出一行一个字符串,表示能获得的字典序最小的DNA序列
数据范围
1 < = n < = 50 , 1 < = 字 符 串 长 度 < = 50 1<=n<=50,1<=字符串长度<=50 1<=n<=50,1<=字符串长度<=50
部分分:存在一种最优解按照 1 到 n 1到n 1到n的顺序连接.
解法
首先考虑部分分,可以发现如果顺序确定,那么可以从
n
到
1
n到1
n到1贪心枚举每个字符串选择的前缀是多少,时间复杂度
O
(
n
4
)
O(n^4)
O(n4),具体就是暴力枚举每个前缀,看放进现有答案是否最优.实现细节:注意每个字符串都要选.
然后问题就在于确定顺序.
对于每个字符串s,找到最短的前缀x,满足
x
i
n
f
<
=
s
x^{inf}<=s
xinf<=s,然后将字符串分成
x
y
+
z
x^y+z
xy+z使得x不是z的前缀.
那么合并的顺序一定是按照
x
i
n
f
x^{inf}
xinf字典序升序然后相同按照
z
+
U
z+U
z+U字典序降序,注意使用字母
U
U
U的原因是
U
U
U比四个字母都要大。
感性的理解就是这样,每次我们希望找个最小的
x
x
x,然后加入尽量多的
x
x
x,但是,加完之后我们希望能加的字符尽量小,所以我们把尾巴字典序最小的放后面。
注意实现细节:计算
x
i
n
f
x^{inf}
xinf时,如果当前的
x
x
x和剩下的长度为
l
e
n
len
len的字符串在len长度内仍然没有比较出大小,那么这个
x
x
x也是合法的.然后就是
z
z
z,
z
z
z的开头会固定在
∣
x
∣
∗
t
+
1
|x|*t+1
∣x∣∗t+1的位置,这个是根据定义来的(这样讲虽然很奇怪,但是实际就是需要满足
x
y
+
z
=
=
s
x^y+z==s
xy+z==s)
#include<bits/stdc++.h>
using namespace std;
inline int read(){
char c=getchar();int t=0,f=1;
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
return t*f;
}
int n,m,pre[55],ord[55],pos[55],len[55];
string s[55],ans,tmp,bt,now,q[55],alfa,st,cpa,cpb,ans2;
int fc(int a,int b){
cpa=q[a],cpb=q[b];
int l1=cpa.size(),l2=cpb.size();
while(cpa.size()!=cpb.size()){
if(cpa.size()<cpb.size())cpa=cpa+q[a];
else cpb=cpb+q[b];
}
int tmp=0;
if(cpa>cpb)tmp=1;
if(cpa<cpb)tmp=-1;
return tmp;
}
bool cmp(int a,int b){
if(fc(a,b)==0)return s[a].substr(pos[a])+'U'>s[b].substr(pos[b])+'U';
else return fc(a,b)==-1;
}
int main(){
//freopen("b.in","r",stdin);
//freopen("b.out","w",stdout);
n=read();
for(int i=1;i<=n;i++){
cin>>s[i];
s[i]=s[i];
m=max(m,(int)s[i].size());
}
if(m==1){
sort(s+1,s+1+n);
for(int i=1;i<=n;i++)putchar(s[i][0]);
return 0;
}
for(int i=1;i<=n;i++){
int l=s[i].size();
for(int j=1;j<=l;j++){
int flag=1;
for(int k=j;k<l;k++){
if(s[i][k]<s[i][k%j]){flag=0;break;}
if(s[i][k]>s[i][k%j]){flag=1;break;}
}
if(flag){q[i]=s[i].substr(0,j);break;}
}
if(!q[i].size()){
q[i]=s[i];
}
int l2=q[i].size(),j;
for(j=l2;j<l;j+=l2){
if(q[i]!=s[i].substr(j,l2)){
pos[i]=j;break;
}
}
if(!pos[i])pos[i]=j;
}
for(int i=1;i<=n;i++)ord[i]=i;
sort(ord+1,ord+1+n,cmp);
/*for(int i=1;i<=n;i++){
cout<<s[ord[i]].substr(pos[ord[i]])<<' '<<s[ord[i]]<<endl;
}*/
ans=s[ord[n]][0];
memset(pre,0,sizeof(pre));
for(int i=n-1;i>0;i--){
int j=1;tmp=s[ord[i]][0]+ans;
bt=tmp;
for(;j<s[ord[i]].size();j++){
tmp=ans;
for(int k=j;k>=0;k--)tmp=s[ord[i]][k]+tmp;
if(bt>tmp){
pre[ord[i]]=j;bt=tmp;
}
}
for(int j=pre[ord[i]];j>=0;j--){
ans=s[ord[i]][j]+ans;
}
}
//if(ans2<ans)ans=ans2;
cout<<ans<<endl;
return 0;
}